@conduction/nextcloud-vue 0.1.0-beta.1

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 (93) hide show
  1. package/dist/nextcloud-vue.cjs.js +10710 -0
  2. package/dist/nextcloud-vue.cjs.js.map +1 -0
  3. package/dist/nextcloud-vue.css +803 -0
  4. package/dist/nextcloud-vue.esm.js +10665 -0
  5. package/dist/nextcloud-vue.esm.js.map +1 -0
  6. package/package.json +63 -0
  7. package/src/components/CnCardGrid/CnCardGrid.vue +152 -0
  8. package/src/components/CnCardGrid/index.js +1 -0
  9. package/src/components/CnCellRenderer/CnCellRenderer.vue +132 -0
  10. package/src/components/CnCellRenderer/index.js +1 -0
  11. package/src/components/CnConfigurationCard/CnConfigurationCard.vue +77 -0
  12. package/src/components/CnConfigurationCard/index.js +1 -0
  13. package/src/components/CnDataTable/CnDataTable.vue +354 -0
  14. package/src/components/CnDataTable/index.js +1 -0
  15. package/src/components/CnDetailViewLayout/CnDetailViewLayout.vue +88 -0
  16. package/src/components/CnDetailViewLayout/index.js +1 -0
  17. package/src/components/CnEmptyState/CnEmptyState.vue +78 -0
  18. package/src/components/CnEmptyState/index.js +1 -0
  19. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +223 -0
  20. package/src/components/CnFacetSidebar/index.js +1 -0
  21. package/src/components/CnFilterBar/CnFilterBar.vue +152 -0
  22. package/src/components/CnFilterBar/index.js +1 -0
  23. package/src/components/CnIndexPage/CnIndexPage.vue +682 -0
  24. package/src/components/CnIndexPage/index.js +1 -0
  25. package/src/components/CnKpiGrid/CnKpiGrid.vue +89 -0
  26. package/src/components/CnKpiGrid/index.js +1 -0
  27. package/src/components/CnListViewLayout/CnListViewLayout.vue +80 -0
  28. package/src/components/CnListViewLayout/index.js +1 -0
  29. package/src/components/CnMassActionBar/CnMassActionBar.vue +160 -0
  30. package/src/components/CnMassActionBar/index.js +1 -0
  31. package/src/components/CnMassCopyDialog/CnMassCopyDialog.vue +320 -0
  32. package/src/components/CnMassCopyDialog/index.js +1 -0
  33. package/src/components/CnMassDeleteDialog/CnMassDeleteDialog.vue +238 -0
  34. package/src/components/CnMassDeleteDialog/index.js +1 -0
  35. package/src/components/CnMassExportDialog/CnMassExportDialog.vue +190 -0
  36. package/src/components/CnMassExportDialog/index.js +1 -0
  37. package/src/components/CnMassImportDialog/CnMassImportDialog.vue +491 -0
  38. package/src/components/CnMassImportDialog/index.js +1 -0
  39. package/src/components/CnObjectCard/CnObjectCard.vue +292 -0
  40. package/src/components/CnObjectCard/index.js +1 -0
  41. package/src/components/CnPagination/CnPagination.vue +252 -0
  42. package/src/components/CnPagination/index.js +1 -0
  43. package/src/components/CnRowActions/CnRowActions.vue +73 -0
  44. package/src/components/CnRowActions/index.js +1 -0
  45. package/src/components/CnSettingsCard/CnSettingsCard.vue +92 -0
  46. package/src/components/CnSettingsCard/index.js +1 -0
  47. package/src/components/CnSettingsSection/CnSettingsSection.vue +266 -0
  48. package/src/components/CnSettingsSection/index.js +1 -0
  49. package/src/components/CnStatsBlock/CnStatsBlock.vue +366 -0
  50. package/src/components/CnStatsBlock/index.js +1 -0
  51. package/src/components/CnStatusBadge/CnStatusBadge.vue +77 -0
  52. package/src/components/CnStatusBadge/index.js +1 -0
  53. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +312 -0
  54. package/src/components/CnVersionInfoCard/index.js +1 -0
  55. package/src/components/CnViewModeToggle/CnViewModeToggle.vue +77 -0
  56. package/src/components/CnViewModeToggle/index.js +1 -0
  57. package/src/components/index.js +25 -0
  58. package/src/composables/index.js +3 -0
  59. package/src/composables/useDetailView.js +132 -0
  60. package/src/composables/useListView.js +153 -0
  61. package/src/composables/useSubResource.js +142 -0
  62. package/src/css/badge.css +51 -0
  63. package/src/css/card.css +128 -0
  64. package/src/css/detail.css +68 -0
  65. package/src/css/index.css +8 -0
  66. package/src/css/layout.css +90 -0
  67. package/src/css/pagination.css +72 -0
  68. package/src/css/table.css +143 -0
  69. package/src/css/utilities.css +46 -0
  70. package/src/index.js +50 -0
  71. package/src/store/createSubResourcePlugin.js +135 -0
  72. package/src/store/index.js +3 -0
  73. package/src/store/plugins/auditTrails.js +17 -0
  74. package/src/store/plugins/files.js +186 -0
  75. package/src/store/plugins/index.js +4 -0
  76. package/src/store/plugins/lifecycle.js +180 -0
  77. package/src/store/plugins/relations.js +68 -0
  78. package/src/store/useObjectStore.js +625 -0
  79. package/src/types/auditTrail.d.ts +32 -0
  80. package/src/types/file.d.ts +23 -0
  81. package/src/types/index.d.ts +35 -0
  82. package/src/types/notification.d.ts +36 -0
  83. package/src/types/object.d.ts +40 -0
  84. package/src/types/organisation.d.ts +41 -0
  85. package/src/types/register.d.ts +25 -0
  86. package/src/types/schema.d.ts +39 -0
  87. package/src/types/shared.d.ts +79 -0
  88. package/src/types/source.d.ts +14 -0
  89. package/src/types/task.d.ts +31 -0
  90. package/src/utils/errors.js +96 -0
  91. package/src/utils/headers.js +44 -0
  92. package/src/utils/index.js +3 -0
  93. package/src/utils/schema.js +287 -0
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Schema utility functions for auto-generating table columns, cell formatting,
3
+ * and faceted filter definitions from OpenRegister schema property definitions.
4
+ *
5
+ * @module utils/schema
6
+ */
7
+
8
+ /**
9
+ * Default column widths per property type/format.
10
+ */
11
+ const DEFAULT_WIDTHS = {
12
+ boolean: '80px',
13
+ integer: '100px',
14
+ number: '100px',
15
+ 'string:uuid': '140px',
16
+ 'string:date-time': '180px',
17
+ 'string:email': '200px',
18
+ }
19
+
20
+ /**
21
+ * Get default width for a property type + format combination.
22
+ *
23
+ * @param {string} type Property type
24
+ * @param {string} [format] Property format
25
+ * @return {string|undefined} CSS width or undefined
26
+ */
27
+ function getDefaultWidth(type, format) {
28
+ if (format) {
29
+ return DEFAULT_WIDTHS[`${type}:${format}`]
30
+ }
31
+ return DEFAULT_WIDTHS[type]
32
+ }
33
+
34
+ /**
35
+ * Generate CnDataTable column definitions from a schema's properties.
36
+ *
37
+ * Reads `schema.properties` and creates column objects sorted by the `order`
38
+ * hint (if present) then alphabetically. Filters out properties marked
39
+ * `visible: false`. Supports include/exclude lists and per-column overrides.
40
+ *
41
+ * @param {object} schema The schema object with a `properties` field
42
+ * @param {object} [options] Configuration options
43
+ * @param {string[]} [options.exclude] Property keys to exclude
44
+ * @param {string[]} [options.include] Property keys to include (whitelist mode)
45
+ * @param {object} [options.overrides] Per-key column overrides, e.g. `{ status: { width: '200px' } }`
46
+ * @return {Array<{key: string, label: string, sortable: boolean, type: string, format: string, width: string}>}
47
+ */
48
+ export function columnsFromSchema(schema, options = {}) {
49
+ const { exclude = [], include = null, overrides = {} } = options
50
+
51
+ if (!schema || !schema.properties) {
52
+ return []
53
+ }
54
+
55
+ const entries = Object.entries(schema.properties)
56
+ .filter(([key, prop]) => {
57
+ // Skip properties marked as not visible
58
+ if (prop.visible === false) return false
59
+ // Apply exclude list
60
+ if (exclude.includes(key)) return false
61
+ // Apply include whitelist
62
+ if (include && !include.includes(key)) return false
63
+ // Skip complex object types by default (they don't render well in tables)
64
+ if (prop.type === 'object') return false
65
+ return true
66
+ })
67
+ .sort(([keyA, propA], [keyB, propB]) => {
68
+ // Sort by order hint first, then alphabetically
69
+ const orderA = typeof propA.order === 'number' ? propA.order : Infinity
70
+ const orderB = typeof propB.order === 'number' ? propB.order : Infinity
71
+ if (orderA !== orderB) return orderA - orderB
72
+ return keyA.localeCompare(keyB)
73
+ })
74
+
75
+ return entries.map(([key, prop]) => {
76
+ const column = {
77
+ key,
78
+ label: prop.title || key,
79
+ sortable: true,
80
+ type: prop.type || 'string',
81
+ format: prop.format || null,
82
+ }
83
+
84
+ // Apply default width
85
+ const defaultWidth = getDefaultWidth(column.type, column.format)
86
+ if (defaultWidth) {
87
+ column.width = defaultWidth
88
+ }
89
+
90
+ // Store enum values for cell renderer
91
+ if (prop.enum) {
92
+ column.enum = prop.enum
93
+ }
94
+
95
+ // Store items type for arrays
96
+ if (prop.items) {
97
+ column.items = prop.items
98
+ }
99
+
100
+ // Apply per-column overrides
101
+ if (overrides[key]) {
102
+ Object.assign(column, overrides[key])
103
+ }
104
+
105
+ return column
106
+ })
107
+ }
108
+
109
+ /**
110
+ * Format a cell value based on its schema property definition.
111
+ *
112
+ * Handles dates, booleans, arrays, numbers, UUIDs, emails, and markdown.
113
+ * Returns a plain string suitable for display in a table cell.
114
+ *
115
+ * @param {*} value The raw value
116
+ * @param {object} [property] The schema property definition `{ type, format, enum, items }`
117
+ * @param {object} [options] Formatting options
118
+ * @param {number} [options.truncate=100] Maximum string length before truncation
119
+ * @return {string} Formatted display string
120
+ */
121
+ export function formatValue(value, property = {}, options = {}) {
122
+ const { truncate = 100 } = options
123
+
124
+ // Null/undefined/empty
125
+ if (value === null || value === undefined || value === '') {
126
+ return '—'
127
+ }
128
+
129
+ const { type = 'string', format } = property
130
+
131
+ // Boolean
132
+ if (type === 'boolean' || typeof value === 'boolean') {
133
+ return value ? '✓' : '—'
134
+ }
135
+
136
+ // Number/Integer
137
+ if (type === 'integer' || type === 'number') {
138
+ const num = Number(value)
139
+ if (Number.isNaN(num)) return String(value)
140
+ return num.toLocaleString()
141
+ }
142
+
143
+ // Array
144
+ if (type === 'array' || Array.isArray(value)) {
145
+ if (!Array.isArray(value)) return String(value)
146
+ if (value.length === 0) return '—'
147
+ // For short arrays, join values
148
+ if (value.length <= 3) {
149
+ return value.join(', ')
150
+ }
151
+ return `${value.slice(0, 3).join(', ')} +${value.length - 3}`
152
+ }
153
+
154
+ // Object (shouldn't normally appear in tables)
155
+ if (type === 'object' || (typeof value === 'object' && value !== null)) {
156
+ return '[Object]'
157
+ }
158
+
159
+ // String types
160
+ const str = String(value)
161
+
162
+ // Date-time
163
+ if (format === 'date-time' || format === 'date') {
164
+ try {
165
+ const date = new Date(str)
166
+ if (Number.isNaN(date.getTime())) return str
167
+ if (format === 'date') {
168
+ return date.toLocaleDateString(undefined, {
169
+ day: '2-digit',
170
+ month: '2-digit',
171
+ year: 'numeric',
172
+ })
173
+ }
174
+ return date.toLocaleDateString(undefined, {
175
+ day: '2-digit',
176
+ month: '2-digit',
177
+ year: 'numeric',
178
+ }) + ', ' + date.toLocaleTimeString(undefined, {
179
+ hour: '2-digit',
180
+ minute: '2-digit',
181
+ second: '2-digit',
182
+ })
183
+ } catch {
184
+ return str
185
+ }
186
+ }
187
+
188
+ // UUID — truncate to first 8 chars
189
+ if (format === 'uuid') {
190
+ if (str.length > 8) {
191
+ return str.substring(0, 8) + '...'
192
+ }
193
+ return str
194
+ }
195
+
196
+ // URI — show truncated
197
+ if (format === 'uri' || format === 'url') {
198
+ try {
199
+ const url = new URL(str)
200
+ return url.hostname + url.pathname.substring(0, 20)
201
+ } catch {
202
+ return truncateString(str, truncate)
203
+ }
204
+ }
205
+
206
+ // Markdown — strip formatting
207
+ if (format === 'markdown') {
208
+ const stripped = str
209
+ .replace(/#{1,6}\s+/g, '') // headings
210
+ .replace(/[*_]{1,3}([^*_]+)[*_]{1,3}/g, '$1') // bold/italic
211
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // links
212
+ .replace(/`{1,3}[^`]*`{1,3}/g, '') // code
213
+ .replace(/\n+/g, ' ') // newlines
214
+ .trim()
215
+ return truncateString(stripped, truncate)
216
+ }
217
+
218
+ // Email — display as-is (no truncation)
219
+ if (format === 'email') {
220
+ return str
221
+ }
222
+
223
+ // Plain string — truncate if needed
224
+ return truncateString(str, truncate)
225
+ }
226
+
227
+ /**
228
+ * Truncate a string to the given length, adding ellipsis if needed.
229
+ *
230
+ * @param {string} str The string to truncate
231
+ * @param {number} maxLength Maximum length
232
+ * @return {string} Truncated string
233
+ */
234
+ function truncateString(str, maxLength) {
235
+ if (str.length <= maxLength) return str
236
+ return str.substring(0, maxLength) + '...'
237
+ }
238
+
239
+ /**
240
+ * Generate faceted filter definitions from a schema's facetable properties.
241
+ *
242
+ * Reads `schema.properties` and creates filter definitions for properties
243
+ * marked with `facetable: true`. Maps property types to appropriate filter
244
+ * widget types (select, checkbox, text).
245
+ *
246
+ * @param {object} schema The schema object with a `properties` field
247
+ * @return {Array<{key: string, label: string, type: string, propertyType: string, options: Array}>}
248
+ */
249
+ export function filtersFromSchema(schema) {
250
+ if (!schema || !schema.properties) {
251
+ return []
252
+ }
253
+
254
+ return Object.entries(schema.properties)
255
+ .filter(([, prop]) => prop.facetable === true)
256
+ .sort(([keyA, propA], [keyB, propB]) => {
257
+ const orderA = typeof propA.order === 'number' ? propA.order : Infinity
258
+ const orderB = typeof propB.order === 'number' ? propB.order : Infinity
259
+ if (orderA !== orderB) return orderA - orderB
260
+ return keyA.localeCompare(keyB)
261
+ })
262
+ .map(([key, prop]) => {
263
+ const filter = {
264
+ key,
265
+ label: prop.title || key,
266
+ propertyType: prop.type || 'string',
267
+ options: [],
268
+ value: null,
269
+ }
270
+
271
+ // Map property type to filter widget type
272
+ if (prop.type === 'boolean') {
273
+ filter.type = 'checkbox'
274
+ } else if (prop.enum) {
275
+ filter.type = 'select'
276
+ filter.options = prop.enum.map((val) => ({
277
+ id: val,
278
+ label: val,
279
+ }))
280
+ } else {
281
+ // Default to select — options loaded dynamically from facet API
282
+ filter.type = 'select'
283
+ }
284
+
285
+ return filter
286
+ })
287
+ }