@conduction/nextcloud-vue 0.1.0-beta.6 → 0.1.0-beta.7

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 (82) hide show
  1. package/dist/nextcloud-vue.cjs.js +13606 -1918
  2. package/dist/nextcloud-vue.cjs.js.map +1 -1
  3. package/dist/nextcloud-vue.css +1238 -270
  4. package/dist/nextcloud-vue.esm.js +13548 -1880
  5. package/dist/nextcloud-vue.esm.js.map +1 -1
  6. package/package.json +9 -4
  7. package/src/components/CnActionsBar/CnActionsBar.vue +6 -1
  8. package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +1 -11
  9. package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +5 -1
  10. package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +1 -1
  11. package/src/components/CnCard/CnCard.vue +415 -0
  12. package/src/components/CnCard/index.js +1 -0
  13. package/src/components/CnCardGrid/CnCardGrid.vue +20 -20
  14. package/src/components/CnChartWidget/CnChartWidget.vue +3 -1
  15. package/src/components/CnCopyDialog/CnCopyDialog.vue +7 -1
  16. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +4 -0
  17. package/src/components/CnDashboardPage/CnDashboardPage.vue +2 -0
  18. package/src/components/CnDataTable/CnDataTable.vue +6 -2
  19. package/src/components/CnDeleteDialog/CnDeleteDialog.vue +7 -1
  20. package/src/components/CnDetailCard/CnDetailCard.vue +12 -1
  21. package/src/components/CnDetailGrid/CnDetailGrid.vue +254 -0
  22. package/src/components/CnDetailGrid/index.js +1 -0
  23. package/src/components/CnDetailPage/CnDetailPage.vue +157 -11
  24. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +3 -1
  25. package/src/components/CnFormDialog/CnFormDialog.vue +934 -920
  26. package/src/components/CnIcon/CnIcon.vue +1 -1
  27. package/src/components/CnIndexPage/CnIndexPage.vue +51 -9
  28. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +37 -9
  29. package/src/components/CnInfoWidget/CnInfoWidget.vue +219 -0
  30. package/src/components/CnInfoWidget/index.js +1 -0
  31. package/src/components/CnJsonViewer/CnJsonViewer.vue +283 -0
  32. package/src/components/CnJsonViewer/index.js +1 -0
  33. package/src/components/CnKpiGrid/CnKpiGrid.vue +5 -1
  34. package/src/components/CnMassCopyDialog/CnMassCopyDialog.vue +7 -1
  35. package/src/components/CnMassDeleteDialog/CnMassDeleteDialog.vue +7 -1
  36. package/src/components/CnMassExportDialog/CnMassExportDialog.vue +1 -1
  37. package/src/components/CnMassImportDialog/CnMassImportDialog.vue +1 -1
  38. package/src/components/CnObjectCard/CnObjectCard.vue +1 -1
  39. package/src/components/CnObjectSidebar/CnAuditTrailTab.vue +368 -0
  40. package/src/components/CnObjectSidebar/CnFilesTab.vue +286 -0
  41. package/src/components/CnObjectSidebar/CnNotesTab.vue +249 -0
  42. package/src/components/CnObjectSidebar/CnObjectSidebar.vue +45 -668
  43. package/src/components/CnObjectSidebar/CnTagsTab.vue +258 -0
  44. package/src/components/CnObjectSidebar/CnTasksTab.vue +482 -0
  45. package/src/components/CnObjectSidebar/index.js +5 -0
  46. package/src/components/CnProgressBar/CnProgressBar.vue +262 -0
  47. package/src/components/CnProgressBar/index.js +1 -0
  48. package/src/components/CnSchemaFormDialog/CnSchemaPropertiesTab.vue +1 -1
  49. package/src/components/CnStatsBlock/CnStatsBlock.vue +27 -11
  50. package/src/components/CnStatsPanel/CnStatsPanel.vue +320 -0
  51. package/src/components/CnStatsPanel/index.js +1 -0
  52. package/src/components/CnStatusBadge/CnStatusBadge.vue +15 -2
  53. package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +5 -1
  54. package/src/components/CnTableWidget/CnTableWidget.vue +332 -0
  55. package/src/components/CnTableWidget/index.js +1 -0
  56. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +36 -1
  57. package/src/components/index.js +11 -0
  58. package/src/composables/useDashboardView.js +58 -12
  59. package/src/composables/useDetailView.js +3 -2
  60. package/src/composables/useListView.js +7 -6
  61. package/src/composables/useSubResource.js +3 -3
  62. package/src/css/badge.css +32 -0
  63. package/src/css/card.css +1 -0
  64. package/src/css/detail-page.css +74 -7
  65. package/src/index.js +16 -0
  66. package/src/mixins/gridLayout.js +118 -0
  67. package/src/store/createCrudStore.js +360 -0
  68. package/src/store/createSubResourcePlugin.js +5 -15
  69. package/src/store/index.js +1 -0
  70. package/src/store/plugins/auditTrails.js +346 -6
  71. package/src/store/plugins/lifecycle.js +4 -4
  72. package/src/store/plugins/registerMapping.js +18 -8
  73. package/src/store/plugins/relations.js +1 -1
  74. package/src/store/plugins/search.js +21 -8
  75. package/src/store/useObjectStore.js +30 -36
  76. package/src/utils/getTheme.js +9 -0
  77. package/src/utils/headers.js +13 -3
  78. package/src/utils/index.js +1 -0
  79. package/src/utils/schema.js +3 -3
  80. package/src/utils/widgetVisibility.js +162 -0
  81. package/src/components/CnObjectCard/eslint-setup.md +0 -235
  82. package/src/components/CnObjectCard/package.json-or.json +0 -132
@@ -0,0 +1,283 @@
1
+ <!--
2
+ CnJsonViewer — Syntax-highlighted code viewer/editor powered by CodeMirror.
3
+
4
+ Supports multiple languages (JSON, XML, HTML, plain text) with optional
5
+ auto-detection. Use `readOnly` for display-only mode.
6
+ -->
7
+ <template>
8
+ <div class="cn-json-viewer">
9
+ <div :class="['cn-json-viewer__codemirror', isDark ? 'cn-json-viewer__codemirror--dark' : 'cn-json-viewer__codemirror--light']">
10
+ <CodeMirror
11
+ v-model="localValue"
12
+ :basic="true"
13
+ :placeholder="resolvedLanguage === 'json' ? '{ &quot;key&quot;: &quot;value&quot; }' : ''"
14
+ :dark="isDark"
15
+ :readonly="readOnly"
16
+ :linter="linterExtension"
17
+ :lang="langExtension"
18
+ :extensions="editorExtensions"
19
+ :tab-size="2"
20
+ :style="{ height }" />
21
+ <NcButton
22
+ v-if="!readOnly && resolvedLanguage === 'json'"
23
+ class="cn-json-viewer__format-btn"
24
+ type="secondary"
25
+ size="small"
26
+ @click="formatJson">
27
+ Format JSON
28
+ </NcButton>
29
+ </div>
30
+ <span v-if="!readOnly && resolvedLanguage === 'json' && !isValidJson(value)" class="cn-json-viewer__error">
31
+ Invalid JSON format
32
+ </span>
33
+ </div>
34
+ </template>
35
+
36
+ <script>
37
+ import { NcButton } from '@nextcloud/vue'
38
+ import CodeMirror from 'vue-codemirror6'
39
+ import { githubLight, githubDark } from '@uiw/codemirror-theme-github'
40
+ import { json as jsonLang, jsonParseLinter as jsonLinter } from '@codemirror/lang-json'
41
+ import { xml as xmlLang } from '@codemirror/lang-xml'
42
+ import { html as htmlLang } from '@codemirror/lang-html'
43
+ import { getTheme } from '../../utils/getTheme.js'
44
+
45
+ /**
46
+ * CnJsonViewer — Syntax-highlighted code viewer/editor.
47
+ *
48
+ * Wraps CodeMirror 6 with support for JSON, XML, HTML, and plain text.
49
+ * Includes syntax highlighting, and optional formatting/validation for JSON.
50
+ * Use `readOnly` for display-only mode.
51
+ *
52
+ * @example Read-only JSON display (default)
53
+ * <CnJsonViewer :value="jsonString" :read-only="true" />
54
+ *
55
+ * @example Auto-detect language from content
56
+ * <CnJsonViewer :value="responseBody" :read-only="true" language="auto" />
57
+ *
58
+ * @example Explicit XML mode
59
+ * <CnJsonViewer :value="xmlString" :read-only="true" language="xml" />
60
+ *
61
+ * @example Editable JSON with custom height
62
+ * <CnJsonViewer :value="jsonString" height="500px" @update:value="onUpdate" />
63
+ */
64
+ export default {
65
+ name: 'CnJsonViewer',
66
+
67
+ components: {
68
+ NcButton,
69
+ CodeMirror,
70
+ },
71
+
72
+ props: {
73
+ /** JSON string to display or edit */
74
+ value: { type: String, default: '' },
75
+ /** When true, the editor is non-editable and hides format button and validation */
76
+ readOnly: { type: Boolean, default: false },
77
+ /** CSS height for the editor container */
78
+ height: { type: String, default: '300px' },
79
+ /**
80
+ * Content language for syntax highlighting.
81
+ * - 'auto': Auto-detect from content (JSON → xml → text) (default)
82
+ * - 'json': JSON with validation and formatting
83
+ * - 'xml': XML/HTML tag highlighting
84
+ * - 'html': Alias for XML highlighting
85
+ * - 'text': Plain text, no highlighting
86
+ */
87
+ language: {
88
+ type: String,
89
+ default: 'auto',
90
+ validator: (v) => ['json', 'xml', 'html', 'text', 'auto'].includes(v),
91
+ },
92
+ },
93
+
94
+ data() {
95
+ return {
96
+ githubLight,
97
+ githubDark,
98
+ }
99
+ },
100
+
101
+ computed: {
102
+ localValue: {
103
+ get() { return this.value },
104
+ set(v) { this.$emit('update:value', v) },
105
+ },
106
+ isDark: {
107
+ get() { return getTheme() === 'dark' },
108
+ },
109
+ theme: {
110
+ get() { return this.isDark ? githubDark : githubLight },
111
+ },
112
+ /**
113
+ * Resolve 'auto' language to a concrete language based on content.
114
+ * @return {string} Resolved language: 'json', 'xml', or 'text'
115
+ */
116
+ resolvedLanguage() {
117
+ if (this.language !== 'auto') {
118
+ return this.language
119
+ }
120
+ const trimmed = (this.value || '').trim()
121
+ if (!trimmed) return 'text'
122
+ try {
123
+ JSON.parse(trimmed)
124
+ return 'json'
125
+ } catch {
126
+ // not JSON
127
+ }
128
+ if (trimmed.charAt(0) === '<' && trimmed.includes('>')) {
129
+ // Detect HTML by doctype or common HTML tags
130
+ if (/<!doctype\s+html/i.test(trimmed) || /<(?:html|head|body|div|span|p|a|script|style|link|meta|form|input|button|table|ul|ol|li|h[1-6]|img|nav|header|footer|main|section|article)\b/i.test(trimmed)) {
131
+ return 'html'
132
+ }
133
+ return 'xml'
134
+ }
135
+ return 'text'
136
+ },
137
+ /**
138
+ * CodeMirror language extension based on resolved language.
139
+ * @return {object|null} Language extension or null for plain text
140
+ */
141
+ langExtension() {
142
+ switch (this.resolvedLanguage) {
143
+ case 'json':
144
+ return jsonLang()
145
+ case 'html':
146
+ return htmlLang()
147
+ case 'xml':
148
+ return xmlLang()
149
+ case 'text':
150
+ default:
151
+ return null
152
+ }
153
+ },
154
+ /**
155
+ * CodeMirror linter extension (only active for JSON in edit mode).
156
+ * @return {object|null} Linter extension or null
157
+ */
158
+ linterExtension() {
159
+ if (this.readOnly) return null
160
+ if (this.resolvedLanguage === 'json') return jsonLinter()
161
+ return null
162
+ },
163
+ /**
164
+ * Combined CodeMirror extensions array.
165
+ * @return {Array} Extensions including theme and optional language
166
+ */
167
+ editorExtensions() {
168
+ const exts = [this.theme]
169
+ if (this.langExtension) exts.push(this.langExtension)
170
+ return exts
171
+ },
172
+ },
173
+
174
+ methods: {
175
+ /**
176
+ * Format the current JSON value with 2-space indentation.
177
+ * Emits 'update:value' with the formatted string and 'format' with the parsed object.
178
+ */
179
+ formatJson() {
180
+ try {
181
+ if (this.value) {
182
+ const parsed = JSON.parse(this.value)
183
+ this.$emit('update:value', JSON.stringify(parsed, null, 2))
184
+ this.$emit('format', parsed)
185
+ }
186
+ } catch {
187
+ // Keep invalid JSON as-is
188
+ }
189
+ },
190
+
191
+ /**
192
+ * Check if a string is valid JSON.
193
+ * @param {string} str - String to validate
194
+ * @return {boolean} True if valid JSON
195
+ */
196
+ isValidJson(str) {
197
+ if (!str || !str.trim()) return false
198
+ try {
199
+ JSON.parse(str)
200
+ return true
201
+ } catch {
202
+ return false
203
+ }
204
+ },
205
+ },
206
+ }
207
+ </script>
208
+
209
+ <style scoped>
210
+ .cn-json-viewer {
211
+ display: flex;
212
+ flex-direction: column;
213
+ gap: 8px;
214
+ }
215
+
216
+ .cn-json-viewer__codemirror {
217
+ border: 1px solid var(--color-border);
218
+ border-radius: var(--border-radius);
219
+ position: relative;
220
+ }
221
+
222
+ .cn-json-viewer__codemirror :deep(.cm-editor) {
223
+ height: 100%;
224
+ outline: none !important;
225
+ }
226
+
227
+ .cn-json-viewer__codemirror :deep(.cm-scroller) {
228
+ overflow: auto;
229
+ }
230
+
231
+ .cn-json-viewer__codemirror :deep(.cm-content) {
232
+ border-radius: 0 !important;
233
+ border: none !important;
234
+ }
235
+
236
+ .cn-json-viewer__codemirror--light > .vue-codemirror {
237
+ border: 1px dotted silver;
238
+ }
239
+
240
+ .cn-json-viewer__codemirror--dark > .vue-codemirror {
241
+ border: 1px dotted grey;
242
+ }
243
+
244
+ /* Text cursor */
245
+ .cn-json-viewer__codemirror :deep(.cm-content) * {
246
+ cursor: text !important;
247
+ }
248
+
249
+ /* PATCH SELECTION COLOR - default selection system does not work */
250
+ /* Selection background — CodeMirror uses .cm-selectionBackground instead of ::selection */
251
+ .cn-json-viewer__codemirror--light :deep(.cm-selectionBackground) {
252
+ background: #3390ff !important;
253
+ }
254
+
255
+ .cn-json-viewer__codemirror--light :deep(.cm-selectionBackground) + .cm-selectionBackground,
256
+ .cn-json-viewer__codemirror--light :deep(.cm-line ::selection) {
257
+ color: white !important;
258
+ }
259
+
260
+ .cn-json-viewer__codemirror--dark :deep(.cm-selectionBackground) {
261
+ background: #3390ff !important;
262
+ }
263
+
264
+ /* Selected text color */
265
+ .cn-json-viewer__codemirror--light :deep(.cm-content ::selection) {
266
+ background: #3390ff !important;
267
+ color: white !important;
268
+ }
269
+
270
+ .cn-json-viewer__codemirror--dark :deep(.cm-content ::selection) {
271
+ background: #3390ff !important;
272
+ color: white !important;
273
+ }
274
+
275
+ .cn-json-viewer__format-btn {
276
+ margin-top: 8px;
277
+ }
278
+
279
+ .cn-json-viewer__error {
280
+ color: var(--color-error);
281
+ font-size: 14px;
282
+ }
283
+ </style>
@@ -0,0 +1 @@
1
+ export { default as CnJsonViewer } from './CnJsonViewer.vue'
@@ -35,12 +35,16 @@ export default {
35
35
  default: 4,
36
36
  validator: (v) => [2, 3, 4].includes(v),
37
37
  },
38
+ gridClass: {
39
+ type: String,
40
+ default: '',
41
+ },
38
42
  },
39
43
 
40
44
  computed: {
41
45
  gridClasses() {
42
46
  return {
43
- [`cn-kpi-grid--cols-${this.columns}`]: true,
47
+ [`cn-kpi-grid--cols-${this.columns} ${this.gridClass}`]: true,
44
48
  }
45
49
  },
46
50
  },
@@ -132,6 +132,11 @@ export default {
132
132
  type: String,
133
133
  default: 'title',
134
134
  },
135
+ /** Optional function to format the item name. Receives the item, returns a string. Overrides nameField when provided. */
136
+ nameFormatter: {
137
+ type: Function,
138
+ default: null,
139
+ },
135
140
  /** Dialog title */
136
141
  dialogTitle: {
137
142
  type: String,
@@ -190,6 +195,7 @@ export default {
190
195
 
191
196
  methods: {
192
197
  getItemName(item) {
198
+ if (this.nameFormatter) return this.nameFormatter(item)
193
199
  return item[this.nameField] || item.name || item.title || item.id
194
200
  },
195
201
 
@@ -240,7 +246,7 @@ export default {
240
246
  * Set the result of the copy operation. Call this from the parent
241
247
  * after the API call completes.
242
248
  *
243
- * @param {{ success?: boolean, error?: string }} resultData
249
+ * @param {{ success?: boolean, error?: string }} resultData - Result data to pass to the dialog
244
250
  * @public
245
251
  */
246
252
  setResult(resultData) {
@@ -119,6 +119,11 @@ export default {
119
119
  type: String,
120
120
  default: 'title',
121
121
  },
122
+ /** Optional function to format the item name. Receives the item, returns a string. Overrides nameField when provided. */
123
+ nameFormatter: {
124
+ type: Function,
125
+ default: null,
126
+ },
122
127
  /** Dialog title */
123
128
  dialogTitle: {
124
129
  type: String,
@@ -166,6 +171,7 @@ export default {
166
171
 
167
172
  methods: {
168
173
  getItemName(item) {
174
+ if (this.nameFormatter) return this.nameFormatter(item)
169
175
  return item[this.nameField] || item.name || item.title || item.id
170
176
  },
171
177
 
@@ -187,7 +193,7 @@ export default {
187
193
  * Set the result of the delete operation. Call this from the parent
188
194
  * after the API call completes.
189
195
  *
190
- * @param {{ success?: boolean, error?: string }} resultData
196
+ * @param {{ success?: boolean, error?: string }} resultData - Result data to pass to the dialog
191
197
  * @public
192
198
  */
193
199
  setResult(resultData) {
@@ -156,7 +156,7 @@ export default {
156
156
 
157
157
  /**
158
158
  * Set the result of the export operation.
159
- * @param {{ success?: boolean, error?: string }} resultData
159
+ * @param {{ success?: boolean, error?: string }} resultData - Result data to pass to the dialog
160
160
  * @public
161
161
  */
162
162
  setResult(resultData) {
@@ -341,7 +341,7 @@ export default {
341
341
 
342
342
  /**
343
343
  * Set the result of the import operation.
344
- * @param {{ success?: boolean, error?: string, summary?: object }} resultData
344
+ * @param {{ success?: boolean, error?: string, summary?: object }} resultData - Result data to pass to the dialog
345
345
  * @public
346
346
  */
347
347
  setResult(resultData) {
@@ -37,7 +37,7 @@
37
37
  <div v-if="metadataFields.length > 0" class="cn-object-card__metadata">
38
38
  <slot name="metadata" :object="object" :fields="metadataFields">
39
39
  <div
40
- v-for="field in metadataFields"
40
+ v-for="field in metadataFields"
41
41
  :key="field.key"
42
42
  class="cn-object-card__meta-item">
43
43
  <span class="cn-object-card__meta-label">{{ field.label }}</span>