@conduction/nextcloud-vue 0.1.0-beta.10 → 0.1.0-beta.12
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/nextcloud-vue.cjs.js +64663 -63467
- package/dist/nextcloud-vue.cjs.js.map +1 -1
- package/dist/nextcloud-vue.css +443 -444
- package/dist/nextcloud-vue.esm.js +64637 -63443
- package/dist/nextcloud-vue.esm.js.map +1 -1
- package/l10n/en.json +164 -0
- package/l10n/nl.json +164 -0
- package/package.json +19 -3
- package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +8 -7
- package/src/components/CnAdvancedFormDialog/CnDataTab.vue +2 -2
- package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +5 -5
- package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +2 -2
- package/src/components/CnCardGrid/CnCardGrid.vue +2 -1
- package/src/components/CnChartWidget/CnChartWidget.vue +29 -1
- package/src/components/CnCopyDialog/CnCopyDialog.vue +15 -6
- package/src/components/CnDashboardPage/CnDashboardPage.vue +5 -4
- package/src/components/CnDetailGrid/CnDetailGrid.vue +3 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +5 -4
- package/src/components/CnFacetSidebar/CnFacetSidebar.vue +3 -2
- package/src/components/CnFilterBar/CnFilterBar.vue +3 -2
- package/src/components/CnFormDialog/CnFormDialog.vue +122 -9
- package/src/components/CnIndexPage/CnIndexPage.vue +1 -0
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +8 -7
- package/src/components/CnJsonViewer/CnJsonViewer.vue +33 -4
- package/src/components/CnMassActionBar/CnMassActionBar.vue +1 -1
- package/src/components/CnMassImportDialog/CnMassImportDialog.vue +2 -2
- package/src/components/CnNotesCard/CnNotesCard.vue +7 -6
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +11 -10
- package/src/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue +3 -2
- package/src/components/CnObjectSidebar/CnAuditTrailTab.vue +8 -7
- package/src/components/CnObjectSidebar/CnFilesTab.vue +6 -5
- package/src/components/CnObjectSidebar/CnNotesTab.vue +8 -7
- package/src/components/CnObjectSidebar/CnObjectSidebar.vue +6 -5
- package/src/components/CnObjectSidebar/CnTagsTab.vue +3 -2
- package/src/components/CnObjectSidebar/CnTasksTab.vue +11 -10
- package/src/components/CnRegisterMapping/CnRegisterMapping.vue +14 -13
- package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +15 -14
- package/src/components/CnSchemaFormDialog/CnSchemaPropertyActions.vue +4 -4
- package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +10 -10
- package/src/components/CnSettingsSection/CnSettingsSection.vue +5 -4
- package/src/components/CnStatsBlock/CnStatsBlock.vue +5 -4
- package/src/components/CnStatsPanel/CnStatsPanel.vue +3 -2
- package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +9 -8
- package/src/components/CnTableWidget/CnTableWidget.vue +3 -2
- package/src/components/CnTasksCard/CnTasksCard.vue +5 -4
- package/src/components/CnTimelineStages/CnTimelineStages.vue +3 -1
- package/src/components/CnUserActionMenu/CnUserActionMenu.vue +7 -6
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +4 -3
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +3 -1
- package/src/index.js +4 -0
- package/src/l10n/index.js +12 -0
- package/src/store/createCrudStore.d.ts +350 -0
- package/src/store/createCrudStore.js +58 -5
- package/src/store/pluginMerge.js +55 -0
- package/src/store/plugins/index.js +1 -0
- package/src/store/plugins/logs.d.ts +22 -0
- package/src/store/plugins/logs.js +172 -0
- package/src/store/useObjectStore.js +19 -49
- package/src/types/index.d.ts +32 -0
- package/src/utils/schema.js +3 -2
|
@@ -114,6 +114,7 @@
|
|
|
114
114
|
</template>
|
|
115
115
|
|
|
116
116
|
<script>
|
|
117
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
117
118
|
import { NcButton, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
|
|
118
119
|
import Pencil from 'vue-material-design-icons/Pencil.vue'
|
|
119
120
|
import Check from 'vue-material-design-icons/Check.vue'
|
|
@@ -236,22 +237,22 @@ export default {
|
|
|
236
237
|
/** Label for the edit button */
|
|
237
238
|
editLabel: {
|
|
238
239
|
type: String,
|
|
239
|
-
default: 'Edit',
|
|
240
|
+
default: () => t('nextcloud-vue', 'Edit'),
|
|
240
241
|
},
|
|
241
242
|
/** Label for the done button (when editing) */
|
|
242
243
|
doneLabel: {
|
|
243
244
|
type: String,
|
|
244
|
-
default: 'Done',
|
|
245
|
+
default: () => t('nextcloud-vue', 'Done'),
|
|
245
246
|
},
|
|
246
247
|
/** Label for the empty state */
|
|
247
248
|
emptyLabel: {
|
|
248
249
|
type: String,
|
|
249
|
-
default: 'No widgets configured',
|
|
250
|
+
default: () => t('nextcloud-vue', 'No widgets configured'),
|
|
250
251
|
},
|
|
251
252
|
/** Label for unavailable widgets */
|
|
252
253
|
unavailableLabel: {
|
|
253
254
|
type: String,
|
|
254
|
-
default: 'Widget not available',
|
|
255
|
+
default: () => t('nextcloud-vue', 'Widget not available'),
|
|
255
256
|
},
|
|
256
257
|
},
|
|
257
258
|
|
|
@@ -52,6 +52,8 @@
|
|
|
52
52
|
</template>
|
|
53
53
|
|
|
54
54
|
<script>
|
|
55
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
56
|
+
|
|
55
57
|
/**
|
|
56
58
|
* CnDetailGrid — Data-driven label-value grid for detail/info sections.
|
|
57
59
|
*
|
|
@@ -132,7 +134,7 @@ export default {
|
|
|
132
134
|
*/
|
|
133
135
|
emptyLabel: {
|
|
134
136
|
type: String,
|
|
135
|
-
default: 'No details available',
|
|
137
|
+
default: () => t('nextcloud-vue', 'No details available'),
|
|
136
138
|
},
|
|
137
139
|
},
|
|
138
140
|
|
|
@@ -158,6 +158,7 @@
|
|
|
158
158
|
</template>
|
|
159
159
|
|
|
160
160
|
<script>
|
|
161
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
161
162
|
import { NcButton, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
|
|
162
163
|
import { CnIcon } from '../CnIcon/index.js'
|
|
163
164
|
import AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'
|
|
@@ -278,7 +279,7 @@ export default {
|
|
|
278
279
|
/** Message shown during loading */
|
|
279
280
|
loadingLabel: {
|
|
280
281
|
type: String,
|
|
281
|
-
default: 'Loading...',
|
|
282
|
+
default: () => t('nextcloud-vue', 'Loading...'),
|
|
282
283
|
},
|
|
283
284
|
/** Whether to activate the external sidebar (via objectSidebarState inject) */
|
|
284
285
|
sidebar: {
|
|
@@ -318,7 +319,7 @@ export default {
|
|
|
318
319
|
/** Error message shown in error state */
|
|
319
320
|
errorMessage: {
|
|
320
321
|
type: String,
|
|
321
|
-
default: 'An error occurred',
|
|
322
|
+
default: () => t('nextcloud-vue', 'An error occurred'),
|
|
322
323
|
},
|
|
323
324
|
/** Callback for retry button in error state. If null, no retry button is shown. */
|
|
324
325
|
onRetry: {
|
|
@@ -328,7 +329,7 @@ export default {
|
|
|
328
329
|
/** Label for the retry button */
|
|
329
330
|
retryLabel: {
|
|
330
331
|
type: String,
|
|
331
|
-
default: 'Retry',
|
|
332
|
+
default: () => t('nextcloud-vue', 'Retry'),
|
|
332
333
|
},
|
|
333
334
|
/** Whether the page has no data to show */
|
|
334
335
|
empty: {
|
|
@@ -338,7 +339,7 @@ export default {
|
|
|
338
339
|
/** Message shown when page is empty */
|
|
339
340
|
emptyLabel: {
|
|
340
341
|
type: String,
|
|
341
|
-
default: 'No data available',
|
|
342
|
+
default: () => t('nextcloud-vue', 'No data available'),
|
|
342
343
|
},
|
|
343
344
|
/** Title shown above the statistics table */
|
|
344
345
|
statsTitle: {
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
</template>
|
|
60
60
|
|
|
61
61
|
<script>
|
|
62
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
62
63
|
import { NcButton, NcSelect, NcTextField, NcCheckboxRadioSwitch, NcLoadingIcon } from '@nextcloud/vue'
|
|
63
64
|
import { filtersFromSchema } from '../../utils/schema.js'
|
|
64
65
|
|
|
@@ -112,12 +113,12 @@ export default {
|
|
|
112
113
|
/** Sidebar title */
|
|
113
114
|
title: {
|
|
114
115
|
type: String,
|
|
115
|
-
default: 'Filters',
|
|
116
|
+
default: () => t('nextcloud-vue', 'Filters'),
|
|
116
117
|
},
|
|
117
118
|
/** Clear all button label */
|
|
118
119
|
clearLabel: {
|
|
119
120
|
type: String,
|
|
120
|
-
default: 'Clear all',
|
|
121
|
+
default: () => t('nextcloud-vue', 'Clear all'),
|
|
121
122
|
},
|
|
122
123
|
/**
|
|
123
124
|
* Whether the current user is an admin.
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
</template>
|
|
64
64
|
|
|
65
65
|
<script>
|
|
66
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
66
67
|
import { NcTextField, NcSelect, NcButton, NcCheckboxRadioSwitch } from '@nextcloud/vue'
|
|
67
68
|
import Magnify from 'vue-material-design-icons/Magnify.vue'
|
|
68
69
|
|
|
@@ -113,7 +114,7 @@ export default {
|
|
|
113
114
|
/** Search input placeholder text */
|
|
114
115
|
searchPlaceholder: {
|
|
115
116
|
type: String,
|
|
116
|
-
default: 'Search...',
|
|
117
|
+
default: () => t('nextcloud-vue', 'Search...'),
|
|
117
118
|
},
|
|
118
119
|
/** Whether to show the "Clear all" button */
|
|
119
120
|
showClearAll: {
|
|
@@ -123,7 +124,7 @@ export default {
|
|
|
123
124
|
/** Clear all button label */
|
|
124
125
|
clearAllLabel: {
|
|
125
126
|
type: String,
|
|
126
|
-
default: 'Clear filters',
|
|
127
|
+
default: () => t('nextcloud-vue', 'Clear filters'),
|
|
127
128
|
},
|
|
128
129
|
},
|
|
129
130
|
|
|
@@ -227,6 +227,43 @@
|
|
|
227
227
|
:disabled="field.readOnly"
|
|
228
228
|
@update:value="value => updateField(field.key, value)" />
|
|
229
229
|
|
|
230
|
+
<!-- JSON (type: 'object'|'array'|... with widget: 'json'): parses on input, stores parsed value in formData -->
|
|
231
|
+
<div v-else-if="field.widget === 'json'" class="cn-form-dialog__json-wrapper">
|
|
232
|
+
<label :for="'cn-form-' + field.key" class="cn-form-dialog__label">
|
|
233
|
+
{{ field.label }}{{ field.required ? ' *' : '' }}
|
|
234
|
+
</label>
|
|
235
|
+
<CnJsonViewer
|
|
236
|
+
:value="jsonStringFor(field)"
|
|
237
|
+
language="json"
|
|
238
|
+
:read-only="field.readOnly"
|
|
239
|
+
:error-text="jsonErrors[field.key] || ''"
|
|
240
|
+
@update:value="value => onJsonFieldInput(field, value)" />
|
|
241
|
+
<span
|
|
242
|
+
v-if="errors[field.key] || field.description"
|
|
243
|
+
class="cn-form-dialog__helper"
|
|
244
|
+
:class="{ 'cn-form-dialog__helper--error': errors[field.key] }">
|
|
245
|
+
{{ errors[field.key] || field.description }}
|
|
246
|
+
</span>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<!-- Code (freeform editor, stored as raw string; optional `field.language` chooses highlighting) -->
|
|
250
|
+
<div v-else-if="field.widget === 'code'" class="cn-form-dialog__json-wrapper">
|
|
251
|
+
<label :for="'cn-form-' + field.key" class="cn-form-dialog__label">
|
|
252
|
+
{{ field.label }}{{ field.required ? ' *' : '' }}
|
|
253
|
+
</label>
|
|
254
|
+
<CnJsonViewer
|
|
255
|
+
:value="formData[field.key] != null ? String(formData[field.key]) : ''"
|
|
256
|
+
:language="field.language || 'auto'"
|
|
257
|
+
:read-only="field.readOnly"
|
|
258
|
+
@update:value="value => updateField(field.key, value)" />
|
|
259
|
+
<span
|
|
260
|
+
v-if="errors[field.key] || field.description"
|
|
261
|
+
class="cn-form-dialog__helper"
|
|
262
|
+
:class="{ 'cn-form-dialog__helper--error': errors[field.key] }">
|
|
263
|
+
{{ errors[field.key] || field.description }}
|
|
264
|
+
</span>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
230
267
|
<!-- Fallback: text input -->
|
|
231
268
|
<NcTextField
|
|
232
269
|
v-else
|
|
@@ -251,7 +288,7 @@
|
|
|
251
288
|
<NcButton
|
|
252
289
|
v-if="result === null"
|
|
253
290
|
type="primary"
|
|
254
|
-
:disabled="loading || !requiredFieldsFilled"
|
|
291
|
+
:disabled="loading || !requiredFieldsFilled || !jsonFieldsValid"
|
|
255
292
|
@click="executeConfirm">
|
|
256
293
|
<template #icon>
|
|
257
294
|
<NcLoadingIcon v-if="loading" :size="20" />
|
|
@@ -265,7 +302,9 @@
|
|
|
265
302
|
</template>
|
|
266
303
|
|
|
267
304
|
<script>
|
|
305
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
268
306
|
import { NcDialog, NcButton, NcNoteCard, NcLoadingIcon, NcTextField, NcSelect, NcCheckboxRadioSwitch } from '@nextcloud/vue'
|
|
307
|
+
import CnJsonViewer from '../CnJsonViewer/CnJsonViewer.vue'
|
|
269
308
|
import Plus from 'vue-material-design-icons/Plus.vue'
|
|
270
309
|
import ContentSaveOutline from 'vue-material-design-icons/ContentSaveOutline.vue'
|
|
271
310
|
import { fieldsFromSchema } from '../../utils/schema.js'
|
|
@@ -297,6 +336,17 @@ import { fieldsFromSchema } from '../../utils/schema.js'
|
|
|
297
336
|
* (with empty query) and on each search input (debounced, default 300ms, configurable
|
|
298
337
|
* via `field.debounce`). Async selects store the full option object in formData.
|
|
299
338
|
*
|
|
339
|
+
* ## JSON / code fields
|
|
340
|
+
*
|
|
341
|
+
* Two widgets render a CnJsonViewer-powered editor:
|
|
342
|
+
*
|
|
343
|
+
* - `widget: 'json'` — Parses on input. formData holds the parsed value (object,
|
|
344
|
+
* array, number, string, boolean, or `null` for empty). Invalid JSON displays an
|
|
345
|
+
* inline error and blocks the confirm button until fixed. Pair with `type: 'object'`
|
|
346
|
+
* (or any type) to opt a property out of the default object-filter in `fieldsFromSchema`.
|
|
347
|
+
* - `widget: 'code'` — Stores the raw string. Optional `field.language` chooses
|
|
348
|
+
* syntax highlighting (`'json'|'xml'|'html'|'text'|'auto'`, default `'auto'`).
|
|
349
|
+
*
|
|
300
350
|
* The dialog does NOT perform the save itself — it emits a `confirm` event
|
|
301
351
|
* with the form data. The parent performs the actual API call and calls
|
|
302
352
|
* `setResult()` via a ref.
|
|
@@ -359,6 +409,7 @@ export default {
|
|
|
359
409
|
NcTextField,
|
|
360
410
|
NcSelect,
|
|
361
411
|
NcCheckboxRadioSwitch,
|
|
412
|
+
CnJsonViewer,
|
|
362
413
|
Plus,
|
|
363
414
|
ContentSaveOutline,
|
|
364
415
|
},
|
|
@@ -414,8 +465,8 @@ export default {
|
|
|
414
465
|
type: String,
|
|
415
466
|
default: '',
|
|
416
467
|
},
|
|
417
|
-
cancelLabel: { type: String, default: 'Cancel' },
|
|
418
|
-
closeLabel: { type: String, default: 'Close' },
|
|
468
|
+
cancelLabel: { type: String, default: () => t('nextcloud-vue', 'Cancel') },
|
|
469
|
+
closeLabel: { type: String, default: () => t('nextcloud-vue', 'Close') },
|
|
419
470
|
/** Confirm button label. Defaults to "Create" or "Save". */
|
|
420
471
|
confirmLabel: {
|
|
421
472
|
type: String,
|
|
@@ -432,6 +483,10 @@ export default {
|
|
|
432
483
|
closeTimeout: null,
|
|
433
484
|
/** Per-field async state: { [fieldKey]: { options: [], loading: false, searchTimeout: null } } */
|
|
434
485
|
asyncState: {},
|
|
486
|
+
/** Per-field editor string for `json` widgets (preserves input between keystrokes even while invalid) */
|
|
487
|
+
jsonDrafts: {},
|
|
488
|
+
/** Per-field parse-error messages for `json` widgets (blocks confirm) */
|
|
489
|
+
jsonErrors: {},
|
|
435
490
|
}
|
|
436
491
|
},
|
|
437
492
|
|
|
@@ -441,24 +496,24 @@ export default {
|
|
|
441
496
|
},
|
|
442
497
|
|
|
443
498
|
schemaTitle() {
|
|
444
|
-
return (this.schema && this.schema.title) || 'Item'
|
|
499
|
+
return (this.schema && this.schema.title) || t('nextcloud-vue', 'Item')
|
|
445
500
|
},
|
|
446
501
|
|
|
447
502
|
resolvedTitle() {
|
|
448
503
|
if (this.dialogTitle) return this.dialogTitle
|
|
449
504
|
return this.isCreateMode
|
|
450
|
-
?
|
|
451
|
-
:
|
|
505
|
+
? t('nextcloud-vue', 'Create {title}', { title: this.schemaTitle })
|
|
506
|
+
: t('nextcloud-vue', 'Edit {title}', { title: this.schemaTitle })
|
|
452
507
|
},
|
|
453
508
|
|
|
454
509
|
resolvedConfirmLabel() {
|
|
455
510
|
if (this.confirmLabel) return this.confirmLabel
|
|
456
|
-
return this.isCreateMode ? 'Create' : 'Save'
|
|
511
|
+
return this.isCreateMode ? t('nextcloud-vue', 'Create') : t('nextcloud-vue', 'Save')
|
|
457
512
|
},
|
|
458
513
|
|
|
459
514
|
resolvedSuccessText() {
|
|
460
515
|
if (this.successText) return this.successText
|
|
461
|
-
return
|
|
516
|
+
return t('nextcloud-vue', '{title} saved successfully.', { title: this.schemaTitle })
|
|
462
517
|
},
|
|
463
518
|
|
|
464
519
|
/** Whether all required fields have a non-empty value */
|
|
@@ -473,6 +528,11 @@ export default {
|
|
|
473
528
|
})
|
|
474
529
|
},
|
|
475
530
|
|
|
531
|
+
/** Whether every `json` widget currently parses successfully */
|
|
532
|
+
jsonFieldsValid() {
|
|
533
|
+
return Object.keys(this.jsonErrors).every((k) => !this.jsonErrors[k])
|
|
534
|
+
},
|
|
535
|
+
|
|
476
536
|
resolvedFields() {
|
|
477
537
|
// Manual fields take priority
|
|
478
538
|
if (this.fields) return this.fields
|
|
@@ -517,6 +577,8 @@ export default {
|
|
|
517
577
|
data[field.key] = false
|
|
518
578
|
} else if (field.widget === 'tags' || field.widget === 'multiselect') {
|
|
519
579
|
data[field.key] = []
|
|
580
|
+
} else if (field.widget === 'code') {
|
|
581
|
+
data[field.key] = ''
|
|
520
582
|
} else {
|
|
521
583
|
data[field.key] = null
|
|
522
584
|
}
|
|
@@ -524,6 +586,8 @@ export default {
|
|
|
524
586
|
this.formData = data
|
|
525
587
|
}
|
|
526
588
|
this.errors = {}
|
|
589
|
+
this.jsonDrafts = {}
|
|
590
|
+
this.jsonErrors = {}
|
|
527
591
|
this.initAsyncFields()
|
|
528
592
|
},
|
|
529
593
|
|
|
@@ -535,6 +599,53 @@ export default {
|
|
|
535
599
|
}
|
|
536
600
|
},
|
|
537
601
|
|
|
602
|
+
/**
|
|
603
|
+
* Resolve the string shown in the CnJsonViewer for a `json`-widget field.
|
|
604
|
+
* Prefers the unparsed draft (so invalid typing isn't clobbered), falling
|
|
605
|
+
* back to a pretty-printed stringification of the parsed value in formData.
|
|
606
|
+
*
|
|
607
|
+
* @param {object} field Field definition.
|
|
608
|
+
* @return {string} JSON string for the editor.
|
|
609
|
+
*/
|
|
610
|
+
jsonStringFor(field) {
|
|
611
|
+
if (Object.prototype.hasOwnProperty.call(this.jsonDrafts, field.key)) {
|
|
612
|
+
return this.jsonDrafts[field.key]
|
|
613
|
+
}
|
|
614
|
+
const value = this.formData[field.key]
|
|
615
|
+
if (value === null || value === undefined) return ''
|
|
616
|
+
try {
|
|
617
|
+
return JSON.stringify(value, null, 2)
|
|
618
|
+
} catch (e) {
|
|
619
|
+
return String(value)
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Handle input in a `json`-widget CnJsonViewer. Parses on the fly:
|
|
625
|
+
* on success, the parsed value lands in formData and any previous error
|
|
626
|
+
* is cleared; on failure, formData keeps its last-known-good value and
|
|
627
|
+
* `jsonErrors[key]` is set, which surfaces inline and disables confirm.
|
|
628
|
+
*
|
|
629
|
+
* @param {object} field Field definition.
|
|
630
|
+
* @param {string} newString Current editor content.
|
|
631
|
+
*/
|
|
632
|
+
onJsonFieldInput(field, newString) {
|
|
633
|
+
this.$set(this.jsonDrafts, field.key, newString)
|
|
634
|
+
const trimmed = (newString || '').trim()
|
|
635
|
+
if (!trimmed) {
|
|
636
|
+
this.updateField(field.key, null)
|
|
637
|
+
this.$delete(this.jsonErrors, field.key)
|
|
638
|
+
return
|
|
639
|
+
}
|
|
640
|
+
try {
|
|
641
|
+
const parsed = JSON.parse(trimmed)
|
|
642
|
+
this.updateField(field.key, parsed)
|
|
643
|
+
this.$delete(this.jsonErrors, field.key)
|
|
644
|
+
} catch (e) {
|
|
645
|
+
this.$set(this.jsonErrors, field.key, t('nextcloud-vue', 'Invalid JSON: {msg}', { msg: e.message }))
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
|
|
538
649
|
getEnumOptions(field) {
|
|
539
650
|
if (!field.enum) return []
|
|
540
651
|
const labels = field.enumLabels || {}
|
|
@@ -835,6 +946,7 @@ export default {
|
|
|
835
946
|
|
|
836
947
|
executeConfirm() {
|
|
837
948
|
if (!this.validate()) return
|
|
949
|
+
if (!this.jsonFieldsValid) return
|
|
838
950
|
|
|
839
951
|
this.loading = true
|
|
840
952
|
/**
|
|
@@ -888,7 +1000,8 @@ export default {
|
|
|
888
1000
|
}
|
|
889
1001
|
|
|
890
1002
|
.cn-form-dialog__textarea-wrapper,
|
|
891
|
-
.cn-form-dialog__select-wrapper
|
|
1003
|
+
.cn-form-dialog__select-wrapper,
|
|
1004
|
+
.cn-form-dialog__json-wrapper {
|
|
892
1005
|
display: flex;
|
|
893
1006
|
flex-direction: column;
|
|
894
1007
|
gap: 4px;
|
|
@@ -164,6 +164,7 @@
|
|
|
164
164
|
</template>
|
|
165
165
|
|
|
166
166
|
<script>
|
|
167
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
167
168
|
import { NcAppSidebar, NcAppSidebarTab, NcTextField, NcSelect, NcCheckboxRadioSwitch, NcPopover, NcButton } from '@nextcloud/vue'
|
|
168
169
|
import Magnify from 'vue-material-design-icons/Magnify.vue'
|
|
169
170
|
import FormatColumns from 'vue-material-design-icons/FormatColumns.vue'
|
|
@@ -272,37 +273,37 @@ export default {
|
|
|
272
273
|
/** Search input placeholder */
|
|
273
274
|
searchPlaceholder: {
|
|
274
275
|
type: String,
|
|
275
|
-
default: 'Type to search...',
|
|
276
|
+
default: () => t('nextcloud-vue', 'Type to search...'),
|
|
276
277
|
},
|
|
277
278
|
/** Search tab label */
|
|
278
279
|
searchTabLabel: {
|
|
279
280
|
type: String,
|
|
280
|
-
default: 'Search',
|
|
281
|
+
default: () => t('nextcloud-vue', 'Search'),
|
|
281
282
|
},
|
|
282
283
|
/** Columns tab label */
|
|
283
284
|
columnsTabLabel: {
|
|
284
285
|
type: String,
|
|
285
|
-
default: 'Columns',
|
|
286
|
+
default: () => t('nextcloud-vue', 'Columns'),
|
|
286
287
|
},
|
|
287
288
|
/** Search section heading */
|
|
288
289
|
searchLabel: {
|
|
289
290
|
type: String,
|
|
290
|
-
default: 'Search',
|
|
291
|
+
default: () => t('nextcloud-vue', 'Search'),
|
|
291
292
|
},
|
|
292
293
|
/** Filters section heading */
|
|
293
294
|
filtersLabel: {
|
|
294
295
|
type: String,
|
|
295
|
-
default: 'Filters',
|
|
296
|
+
default: () => t('nextcloud-vue', 'Filters'),
|
|
296
297
|
},
|
|
297
298
|
/** Columns section heading */
|
|
298
299
|
columnsHeading: {
|
|
299
300
|
type: String,
|
|
300
|
-
default: 'Column
|
|
301
|
+
default: () => t('nextcloud-vue', 'Column visibility'),
|
|
301
302
|
},
|
|
302
303
|
/** Columns section description */
|
|
303
304
|
columnsDescription: {
|
|
304
305
|
type: String,
|
|
305
|
-
default: 'Select which columns to display in the table',
|
|
306
|
+
default: () => t('nextcloud-vue', 'Select which columns to display in the table'),
|
|
306
307
|
},
|
|
307
308
|
/** Override label for the schema properties group. Defaults to schema.title. */
|
|
308
309
|
propertiesGroupLabel: {
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
Format JSON
|
|
28
28
|
</NcButton>
|
|
29
29
|
</div>
|
|
30
|
-
<span v-if="
|
|
31
|
-
|
|
30
|
+
<span v-if="shouldShowError" class="cn-json-viewer__error">
|
|
31
|
+
{{ resolvedErrorText }}
|
|
32
32
|
</span>
|
|
33
33
|
</div>
|
|
34
34
|
</template>
|
|
@@ -89,6 +89,15 @@ export default {
|
|
|
89
89
|
default: 'auto',
|
|
90
90
|
validator: (v) => ['json', 'xml', 'html', 'text', 'auto'].includes(v),
|
|
91
91
|
},
|
|
92
|
+
/**
|
|
93
|
+
* Custom text for the error banner rendered below the editor.
|
|
94
|
+
* - `null` (default): the built-in "Invalid JSON format" banner renders
|
|
95
|
+
* whenever `language === 'json'` and the content fails to parse.
|
|
96
|
+
* - Any string: the caller owns the banner — it renders when this
|
|
97
|
+
* string is non-empty, and is hidden when empty. Use this to surface
|
|
98
|
+
* a richer parse error (e.g. the exception message).
|
|
99
|
+
*/
|
|
100
|
+
errorText: { type: String, default: null },
|
|
92
101
|
},
|
|
93
102
|
|
|
94
103
|
data() {
|
|
@@ -169,6 +178,28 @@ export default {
|
|
|
169
178
|
if (this.langExtension) exts.push(this.langExtension)
|
|
170
179
|
return exts
|
|
171
180
|
},
|
|
181
|
+
/**
|
|
182
|
+
* Error text displayed in the banner. Caller-provided `errorText` wins;
|
|
183
|
+
* otherwise falls back to the built-in "Invalid JSON format" message.
|
|
184
|
+
* @return {string} Message to show.
|
|
185
|
+
*/
|
|
186
|
+
resolvedErrorText() {
|
|
187
|
+
if (this.errorText !== null) return this.errorText
|
|
188
|
+
return 'Invalid JSON format'
|
|
189
|
+
},
|
|
190
|
+
/**
|
|
191
|
+
* Whether to show the error banner.
|
|
192
|
+
* - If `errorText` is supplied, the caller controls visibility via its
|
|
193
|
+
* emptiness.
|
|
194
|
+
* - Otherwise, show iff the content is editable JSON and fails to parse.
|
|
195
|
+
* @return {boolean} Visibility flag.
|
|
196
|
+
*/
|
|
197
|
+
shouldShowError() {
|
|
198
|
+
if (this.errorText !== null) return this.errorText !== ''
|
|
199
|
+
return !this.readOnly
|
|
200
|
+
&& this.resolvedLanguage === 'json'
|
|
201
|
+
&& !this.isValidJson(this.value)
|
|
202
|
+
},
|
|
172
203
|
},
|
|
173
204
|
|
|
174
205
|
methods: {
|
|
@@ -214,8 +245,6 @@ export default {
|
|
|
214
245
|
}
|
|
215
246
|
|
|
216
247
|
.cn-json-viewer__codemirror {
|
|
217
|
-
border: 1px solid var(--color-border);
|
|
218
|
-
border-radius: var(--border-radius);
|
|
219
248
|
position: relative;
|
|
220
249
|
}
|
|
221
250
|
|
|
@@ -144,7 +144,7 @@ export default {
|
|
|
144
144
|
/** Label template for the menu button. Use {count} for the count. */
|
|
145
145
|
menuLabelTemplate: {
|
|
146
146
|
type: String,
|
|
147
|
-
default: () => t('nextcloud-vue', 'Mass
|
|
147
|
+
default: () => t('nextcloud-vue', 'Mass actions ({count})'),
|
|
148
148
|
},
|
|
149
149
|
importLabel: { type: String, default: () => t('nextcloud-vue', 'Import') },
|
|
150
150
|
exportLabel: { type: String, default: () => t('nextcloud-vue', 'Export') },
|
|
@@ -228,7 +228,7 @@ export default {
|
|
|
228
228
|
/** Dialog title */
|
|
229
229
|
dialogTitle: {
|
|
230
230
|
type: String,
|
|
231
|
-
default: 'Import
|
|
231
|
+
default: () => t('nextcloud-vue', 'Import data'),
|
|
232
232
|
},
|
|
233
233
|
/** Accepted file types (input accept attribute) */
|
|
234
234
|
acceptedTypes: {
|
|
@@ -271,7 +271,7 @@ export default {
|
|
|
271
271
|
},
|
|
272
272
|
summaryTitle: { type: String, default: () => t('nextcloud-vue', 'Import summary') },
|
|
273
273
|
supportedFormatsLabel: { type: String, default: () => t('nextcloud-vue', 'Supported file types:') },
|
|
274
|
-
selectFileLabel: { type: String, default: () => t('nextcloud-vue', 'Select
|
|
274
|
+
selectFileLabel: { type: String, default: () => t('nextcloud-vue', 'Select file') },
|
|
275
275
|
cancelLabel: { type: String, default: () => t('nextcloud-vue', 'Cancel') },
|
|
276
276
|
closeLabel: { type: String, default: () => t('nextcloud-vue', 'Close') },
|
|
277
277
|
confirmLabel: { type: String, default: () => t('nextcloud-vue', 'Import') },
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
</template>
|
|
84
84
|
|
|
85
85
|
<script>
|
|
86
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
86
87
|
import { NcButton, NcLoadingIcon } from '@nextcloud/vue'
|
|
87
88
|
import CommentTextOutline from 'vue-material-design-icons/CommentTextOutline.vue'
|
|
88
89
|
import Send from 'vue-material-design-icons/Send.vue'
|
|
@@ -158,12 +159,12 @@ export default {
|
|
|
158
159
|
},
|
|
159
160
|
|
|
160
161
|
// --- Pre-translated labels ---
|
|
161
|
-
titleLabel: { type: String, default: 'Notes' },
|
|
162
|
-
addNoteLabel: { type: String, default: 'Add note' },
|
|
163
|
-
addNotePlaceholder: { type: String, default: 'Write a note...' },
|
|
164
|
-
noNotesLabel: { type: String, default: 'No notes yet' },
|
|
165
|
-
showAllLabel: { type: String, default: 'Show all' },
|
|
166
|
-
deleteLabel: { type: String, default: 'Delete note' },
|
|
162
|
+
titleLabel: { type: String, default: () => t('nextcloud-vue', 'Notes') },
|
|
163
|
+
addNoteLabel: { type: String, default: () => t('nextcloud-vue', 'Add note') },
|
|
164
|
+
addNotePlaceholder: { type: String, default: () => t('nextcloud-vue', 'Write a note...') },
|
|
165
|
+
noNotesLabel: { type: String, default: () => t('nextcloud-vue', 'No notes yet') },
|
|
166
|
+
showAllLabel: { type: String, default: () => t('nextcloud-vue', 'Show all') },
|
|
167
|
+
deleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete note') },
|
|
167
168
|
},
|
|
168
169
|
|
|
169
170
|
emits: ['note-added', 'note-deleted', 'show-all'],
|
|
@@ -219,6 +219,7 @@
|
|
|
219
219
|
</template>
|
|
220
220
|
|
|
221
221
|
<script>
|
|
222
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
222
223
|
import { NcButton, NcLoadingIcon, NcTextField, NcSelect, NcCheckboxRadioSwitch } from '@nextcloud/vue'
|
|
223
224
|
import { CnDetailCard } from '../CnDetailCard/index.js'
|
|
224
225
|
import ContentSaveOutline from 'vue-material-design-icons/ContentSaveOutline.vue'
|
|
@@ -274,7 +275,7 @@ export default {
|
|
|
274
275
|
/** Widget title shown in the card header */
|
|
275
276
|
title: {
|
|
276
277
|
type: String,
|
|
277
|
-
default: 'Data',
|
|
278
|
+
default: () => t('nextcloud-vue', 'Data'),
|
|
278
279
|
},
|
|
279
280
|
/** Optional MDI icon component for the header */
|
|
280
281
|
icon: {
|
|
@@ -326,7 +327,7 @@ export default {
|
|
|
326
327
|
* - `label` (string) — Override the display label
|
|
327
328
|
* - `widget` (string) — Override the widget type for editing
|
|
328
329
|
*
|
|
329
|
-
* @type {
|
|
330
|
+
* @type {{ [key: string]: { order?: number, gridColumn?: number, gridRow?: number, hidden?: boolean, editable?: boolean, label?: string, widget?: string } }}
|
|
330
331
|
*/
|
|
331
332
|
overrides: {
|
|
332
333
|
type: Object,
|
|
@@ -366,17 +367,17 @@ export default {
|
|
|
366
367
|
/** Label for the save button */
|
|
367
368
|
saveLabel: {
|
|
368
369
|
type: String,
|
|
369
|
-
default: 'Save',
|
|
370
|
+
default: () => t('nextcloud-vue', 'Save'),
|
|
370
371
|
},
|
|
371
372
|
/** Label for the discard button */
|
|
372
373
|
discardLabel: {
|
|
373
374
|
type: String,
|
|
374
|
-
default: 'Discard',
|
|
375
|
+
default: () => t('nextcloud-vue', 'Discard'),
|
|
375
376
|
},
|
|
376
377
|
/** Label shown when no properties to display */
|
|
377
378
|
emptyLabel: {
|
|
378
379
|
type: String,
|
|
379
|
-
default: 'No data available',
|
|
380
|
+
default: () => t('nextcloud-vue', 'No data available'),
|
|
380
381
|
},
|
|
381
382
|
},
|
|
382
383
|
|
|
@@ -502,7 +503,7 @@ export default {
|
|
|
502
503
|
methods: {
|
|
503
504
|
/**
|
|
504
505
|
* Check if a field is editable.
|
|
505
|
-
* @param field
|
|
506
|
+
* @param {object} field - Resolved field definition from resolvedFields
|
|
506
507
|
*/
|
|
507
508
|
isEditable(field) {
|
|
508
509
|
if (!this.editable) return false
|
|
@@ -517,7 +518,7 @@ export default {
|
|
|
517
518
|
|
|
518
519
|
/**
|
|
519
520
|
* Check if a field's current value is empty.
|
|
520
|
-
* @param key
|
|
521
|
+
* @param {string} key - Field key to check
|
|
521
522
|
*/
|
|
522
523
|
isValueEmpty(key) {
|
|
523
524
|
const val = key in this.dirtyFields
|
|
@@ -528,7 +529,7 @@ export default {
|
|
|
528
529
|
|
|
529
530
|
/**
|
|
530
531
|
* Start inline editing for a field.
|
|
531
|
-
* @param field
|
|
532
|
+
* @param {object} field - Resolved field definition from resolvedFields
|
|
532
533
|
*/
|
|
533
534
|
startEdit(field) {
|
|
534
535
|
// Set working value: dirty value > current object value
|
|
@@ -555,8 +556,8 @@ export default {
|
|
|
555
556
|
|
|
556
557
|
/**
|
|
557
558
|
* Update the working edit value for a field.
|
|
558
|
-
* @param key
|
|
559
|
-
* @param value
|
|
559
|
+
* @param {string} key - Field key to update
|
|
560
|
+
* @param {*} value - New value for the field
|
|
560
561
|
*/
|
|
561
562
|
updateField(key, value) {
|
|
562
563
|
this.editData = { ...this.editData, [key]: value }
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
</template>
|
|
28
28
|
|
|
29
29
|
<script>
|
|
30
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
30
31
|
import { CnDetailCard } from '../CnDetailCard/index.js'
|
|
31
32
|
import { CnDetailGrid } from '../CnDetailGrid/index.js'
|
|
32
33
|
|
|
@@ -87,7 +88,7 @@ export default {
|
|
|
87
88
|
/** Widget title shown in the card header */
|
|
88
89
|
title: {
|
|
89
90
|
type: String,
|
|
90
|
-
default: 'Metadata',
|
|
91
|
+
default: () => t('nextcloud-vue', 'Metadata'),
|
|
91
92
|
},
|
|
92
93
|
/** Optional MDI icon component for the header */
|
|
93
94
|
icon: {
|
|
@@ -160,7 +161,7 @@ export default {
|
|
|
160
161
|
/** Label shown when no metadata available */
|
|
161
162
|
emptyLabel: {
|
|
162
163
|
type: String,
|
|
163
|
-
default: 'No metadata available',
|
|
164
|
+
default: () => t('nextcloud-vue', 'No metadata available'),
|
|
164
165
|
},
|
|
165
166
|
},
|
|
166
167
|
|