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

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 (54) hide show
  1. package/README.md +226 -0
  2. package/dist/nextcloud-vue.cjs.js +7039 -2409
  3. package/dist/nextcloud-vue.cjs.js.map +1 -1
  4. package/dist/nextcloud-vue.css +237 -52
  5. package/dist/nextcloud-vue.esm.js +7012 -2386
  6. package/dist/nextcloud-vue.esm.js.map +1 -1
  7. package/package.json +2 -4
  8. package/src/components/CnActionsBar/CnActionsBar.vue +225 -0
  9. package/src/components/CnActionsBar/index.js +1 -0
  10. package/src/components/CnCopyDialog/CnCopyDialog.vue +250 -0
  11. package/src/components/CnCopyDialog/index.js +1 -0
  12. package/src/components/CnDataTable/CnDataTable.vue +0 -5
  13. package/src/components/CnDeleteDialog/CnDeleteDialog.vue +170 -0
  14. package/src/components/CnDeleteDialog/index.js +1 -0
  15. package/src/components/CnFormDialog/CnFormDialog.vue +629 -0
  16. package/src/components/CnFormDialog/index.js +1 -0
  17. package/src/components/CnIcon/CnIcon.vue +89 -0
  18. package/src/components/CnIcon/index.js +1 -0
  19. package/src/components/CnIndexPage/CnIndexPage.vue +434 -300
  20. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +484 -0
  21. package/src/components/CnIndexSidebar/index.js +1 -0
  22. package/src/components/CnPageHeader/CnPageHeader.vue +57 -0
  23. package/src/components/CnPageHeader/index.js +1 -0
  24. package/src/components/CnRegisterMapping/CnRegisterMapping.vue +792 -0
  25. package/src/components/CnRegisterMapping/index.js +1 -0
  26. package/src/components/index.js +8 -4
  27. package/src/constants/metadata.js +30 -0
  28. package/src/css/actions-bar.css +48 -0
  29. package/src/css/badge.css +4 -4
  30. package/src/css/card.css +23 -23
  31. package/src/css/detail.css +13 -13
  32. package/src/css/index-page.css +32 -0
  33. package/src/css/index-sidebar.css +187 -0
  34. package/src/css/index.css +4 -0
  35. package/src/css/layout.css +14 -14
  36. package/src/css/page-header.css +33 -0
  37. package/src/css/pagination.css +12 -12
  38. package/src/css/table.css +21 -22
  39. package/src/css/utilities.css +2 -2
  40. package/src/index.js +11 -8
  41. package/src/store/plugins/index.js +1 -0
  42. package/src/store/plugins/registerMapping.js +185 -0
  43. package/src/store/useObjectStore.js +122 -61
  44. package/src/utils/headers.js +7 -1
  45. package/src/utils/index.js +1 -1
  46. package/src/utils/schema.js +133 -1
  47. package/src/components/CnDetailViewLayout/CnDetailViewLayout.vue +0 -88
  48. package/src/components/CnDetailViewLayout/index.js +0 -1
  49. package/src/components/CnEmptyState/CnEmptyState.vue +0 -78
  50. package/src/components/CnEmptyState/index.js +0 -1
  51. package/src/components/CnListViewLayout/CnListViewLayout.vue +0 -80
  52. package/src/components/CnListViewLayout/index.js +0 -1
  53. package/src/components/CnViewModeToggle/CnViewModeToggle.vue +0 -77
  54. package/src/components/CnViewModeToggle/index.js +0 -1
@@ -0,0 +1,629 @@
1
+ <template>
2
+ <NcDialog
3
+ :name="resolvedTitle"
4
+ :size="size"
5
+ :can-close="!loading"
6
+ @closing="$emit('close')">
7
+ <!-- Result phase -->
8
+ <div v-if="result !== null" class="cn-form-dialog__result">
9
+ <NcNoteCard v-if="result.success" type="success">
10
+ {{ resolvedSuccessText }}
11
+ </NcNoteCard>
12
+ <NcNoteCard v-if="result.error" type="error">
13
+ {{ result.error }}
14
+ </NcNoteCard>
15
+ </div>
16
+
17
+ <!-- Form phase -->
18
+ <div v-else class="cn-form-dialog__form">
19
+ <!-- Full form override slot -->
20
+ <slot
21
+ v-if="$scopedSlots.form"
22
+ name="form"
23
+ :fields="resolvedFields"
24
+ :form-data="formData"
25
+ :errors="errors"
26
+ :update-field="updateField" />
27
+
28
+ <!-- Auto-generated form -->
29
+ <template v-else>
30
+ <slot name="before-fields" />
31
+
32
+ <div
33
+ v-for="field in resolvedFields"
34
+ :key="field.key"
35
+ class="cn-form-dialog__field">
36
+ <!-- Per-field override slot -->
37
+ <slot
38
+ v-if="$scopedSlots['field-' + field.key]"
39
+ :name="'field-' + field.key"
40
+ :field="field"
41
+ :value="formData[field.key]"
42
+ :error="errors[field.key]"
43
+ :update-field="updateField" />
44
+
45
+ <!-- Auto-generated field -->
46
+ <template v-else>
47
+ <!-- Text / Email / URL -->
48
+ <NcTextField
49
+ v-if="field.widget === 'text' || field.widget === 'email' || field.widget === 'url'"
50
+ :label="field.label + (field.required ? ' *' : '')"
51
+ :value="formData[field.key] != null ? String(formData[field.key]) : ''"
52
+ :helper-text="errors[field.key] || field.description"
53
+ :error="!!errors[field.key]"
54
+ :type="field.widget === 'email' ? 'email' : field.widget === 'url' ? 'url' : 'text'"
55
+ :disabled="field.readOnly"
56
+ :placeholder="field.description"
57
+ @update:value="value => updateField(field.key, value)" />
58
+
59
+ <!-- Number -->
60
+ <NcTextField
61
+ v-else-if="field.widget === 'number'"
62
+ :label="field.label + (field.required ? ' *' : '')"
63
+ :value="formData[field.key] != null ? String(formData[field.key]) : ''"
64
+ :helper-text="errors[field.key] || field.description"
65
+ :error="!!errors[field.key]"
66
+ type="number"
67
+ :disabled="field.readOnly"
68
+ :placeholder="field.description"
69
+ @update:value="value => updateField(field.key, value !== '' ? Number(value) : null)" />
70
+
71
+ <!-- Textarea -->
72
+ <div v-else-if="field.widget === 'textarea'" class="cn-form-dialog__textarea-wrapper">
73
+ <label :for="'cn-form-' + field.key" class="cn-form-dialog__label">
74
+ {{ field.label }}{{ field.required ? ' *' : '' }}
75
+ </label>
76
+ <textarea
77
+ :id="'cn-form-' + field.key"
78
+ class="cn-form-dialog__textarea"
79
+ :value="formData[field.key] || ''"
80
+ :disabled="field.readOnly"
81
+ :placeholder="field.description"
82
+ rows="4"
83
+ @input="updateField(field.key, $event.target.value)" />
84
+ <span
85
+ v-if="errors[field.key] || field.description"
86
+ class="cn-form-dialog__helper"
87
+ :class="{ 'cn-form-dialog__helper--error': errors[field.key] }">
88
+ {{ errors[field.key] || field.description }}
89
+ </span>
90
+ </div>
91
+
92
+ <!-- Select (enum) -->
93
+ <div v-else-if="field.widget === 'select'" class="cn-form-dialog__select-wrapper">
94
+ <label :for="'cn-form-' + field.key" class="cn-form-dialog__label">
95
+ {{ field.label }}{{ field.required ? ' *' : '' }}
96
+ </label>
97
+ <NcSelect
98
+ :input-id="'cn-form-' + field.key"
99
+ :options="getEnumOptions(field)"
100
+ :value="getSelectedEnumOption(field)"
101
+ :clearable="!field.required"
102
+ :disabled="field.readOnly"
103
+ @input="onSelectChange(field.key, $event)" />
104
+ <span
105
+ v-if="errors[field.key] || field.description"
106
+ class="cn-form-dialog__helper"
107
+ :class="{ 'cn-form-dialog__helper--error': errors[field.key] }">
108
+ {{ errors[field.key] || field.description }}
109
+ </span>
110
+ </div>
111
+
112
+ <!-- Multiselect (array with enum items) -->
113
+ <div v-else-if="field.widget === 'multiselect'" class="cn-form-dialog__select-wrapper">
114
+ <label :for="'cn-form-' + field.key" class="cn-form-dialog__label">
115
+ {{ field.label }}{{ field.required ? ' *' : '' }}
116
+ </label>
117
+ <NcSelect
118
+ :input-id="'cn-form-' + field.key"
119
+ :options="getArrayEnumOptions(field)"
120
+ :value="getSelectedArrayOptions(field)"
121
+ :multiple="true"
122
+ :clearable="true"
123
+ :disabled="field.readOnly"
124
+ @input="onMultiSelectChange(field.key, $event)" />
125
+ <span
126
+ v-if="errors[field.key] || field.description"
127
+ class="cn-form-dialog__helper"
128
+ :class="{ 'cn-form-dialog__helper--error': errors[field.key] }">
129
+ {{ errors[field.key] || field.description }}
130
+ </span>
131
+ </div>
132
+
133
+ <!-- Tags (array, freeform) -->
134
+ <div v-else-if="field.widget === 'tags'" class="cn-form-dialog__select-wrapper">
135
+ <label :for="'cn-form-' + field.key" class="cn-form-dialog__label">
136
+ {{ field.label }}{{ field.required ? ' *' : '' }}
137
+ </label>
138
+ <NcSelect
139
+ :input-id="'cn-form-' + field.key"
140
+ :value="formData[field.key] || []"
141
+ :multiple="true"
142
+ :taggable="true"
143
+ :clearable="true"
144
+ :disabled="field.readOnly"
145
+ @input="updateField(field.key, $event)" />
146
+ <span
147
+ v-if="errors[field.key] || field.description"
148
+ class="cn-form-dialog__helper"
149
+ :class="{ 'cn-form-dialog__helper--error': errors[field.key] }">
150
+ {{ errors[field.key] || field.description }}
151
+ </span>
152
+ </div>
153
+
154
+ <!-- Checkbox / Switch (boolean) -->
155
+ <NcCheckboxRadioSwitch
156
+ v-else-if="field.widget === 'checkbox'"
157
+ :checked="!!formData[field.key]"
158
+ :disabled="field.readOnly"
159
+ type="switch"
160
+ @update:checked="value => updateField(field.key, value)">
161
+ {{ field.label }}{{ field.required ? ' *' : '' }}
162
+ </NcCheckboxRadioSwitch>
163
+
164
+ <!-- Date -->
165
+ <NcTextField
166
+ v-else-if="field.widget === 'date'"
167
+ :label="field.label + (field.required ? ' *' : '')"
168
+ :value="formData[field.key] || ''"
169
+ :helper-text="errors[field.key] || field.description"
170
+ :error="!!errors[field.key]"
171
+ type="date"
172
+ :disabled="field.readOnly"
173
+ @update:value="value => updateField(field.key, value)" />
174
+
175
+ <!-- Datetime -->
176
+ <NcTextField
177
+ v-else-if="field.widget === 'datetime'"
178
+ :label="field.label + (field.required ? ' *' : '')"
179
+ :value="formData[field.key] || ''"
180
+ :helper-text="errors[field.key] || field.description"
181
+ :error="!!errors[field.key]"
182
+ type="datetime-local"
183
+ :disabled="field.readOnly"
184
+ @update:value="value => updateField(field.key, value)" />
185
+
186
+ <!-- Fallback: text input -->
187
+ <NcTextField
188
+ v-else
189
+ :label="field.label + (field.required ? ' *' : '')"
190
+ :value="formData[field.key] != null ? String(formData[field.key]) : ''"
191
+ :helper-text="errors[field.key] || field.description"
192
+ :error="!!errors[field.key]"
193
+ :disabled="field.readOnly"
194
+ :placeholder="field.description"
195
+ @update:value="value => updateField(field.key, value)" />
196
+ </template>
197
+ </div>
198
+
199
+ <slot name="after-fields" />
200
+ </template>
201
+ </div>
202
+
203
+ <template #actions>
204
+ <NcButton @click="$emit('close')">
205
+ {{ result !== null ? closeLabel : cancelLabel }}
206
+ </NcButton>
207
+ <NcButton
208
+ v-if="result === null"
209
+ type="primary"
210
+ :disabled="loading"
211
+ @click="executeConfirm">
212
+ <template #icon>
213
+ <NcLoadingIcon v-if="loading" :size="20" />
214
+ <Plus v-else-if="isCreateMode" :size="20" />
215
+ <ContentSaveOutline v-else :size="20" />
216
+ </template>
217
+ {{ resolvedConfirmLabel }}
218
+ </NcButton>
219
+ </template>
220
+ </NcDialog>
221
+ </template>
222
+
223
+ <script>
224
+ import { NcDialog, NcButton, NcNoteCard, NcLoadingIcon, NcTextField, NcSelect, NcCheckboxRadioSwitch } from '@nextcloud/vue'
225
+ import Plus from 'vue-material-design-icons/Plus.vue'
226
+ import ContentSaveOutline from 'vue-material-design-icons/ContentSaveOutline.vue'
227
+ import { fieldsFromSchema } from '../../utils/schema.js'
228
+
229
+ /**
230
+ * CnFormDialog — Create/edit dialog with auto-generated form from schema.
231
+ *
232
+ * When `item` is null, operates in create mode. When `item` is provided,
233
+ * operates in edit mode. Auto-generates form fields from schema using
234
+ * `fieldsFromSchema()`, but supports slot overrides at three levels:
235
+ *
236
+ * - `#form` — Replace the entire form content
237
+ * - `#field-{key}` — Replace a single auto-generated field
238
+ * - `#before-fields` / `#after-fields` — Inject content around fields
239
+ *
240
+ * The dialog does NOT perform the save itself — it emits a `confirm` event
241
+ * with the form data. The parent performs the actual API call and calls
242
+ * `setResult()` via a ref.
243
+ *
244
+ * @event confirm Emitted when the user confirms the form. Payload: formData object (includes `id` in edit mode).
245
+ * @event close Emitted when the dialog should be closed (cancel, close button, or auto-close after success).
246
+ *
247
+ * @example
248
+ * <CnFormDialog
249
+ * v-if="showFormDialog"
250
+ * ref="formDialog"
251
+ * :schema="schema"
252
+ * :item="editItem"
253
+ * @confirm="onFormConfirm"
254
+ * @close="showFormDialog = false" />
255
+ *
256
+ * // In methods:
257
+ * async onFormConfirm(formData) {
258
+ * try {
259
+ * if (formData.id) {
260
+ * await store.updateItem(formData.id, formData)
261
+ * } else {
262
+ * await store.createItem(formData)
263
+ * }
264
+ * this.$refs.formDialog.setResult({ success: true })
265
+ * } catch (e) {
266
+ * this.$refs.formDialog.setResult({ error: e.message })
267
+ * }
268
+ * }
269
+ */
270
+ export default {
271
+ name: 'CnFormDialog',
272
+
273
+ components: {
274
+ NcDialog,
275
+ NcButton,
276
+ NcNoteCard,
277
+ NcLoadingIcon,
278
+ NcTextField,
279
+ NcSelect,
280
+ NcCheckboxRadioSwitch,
281
+ Plus,
282
+ ContentSaveOutline,
283
+ },
284
+
285
+ props: {
286
+ /** Schema for auto-generating fields. Either schema or fields must be provided. */
287
+ schema: {
288
+ type: Object,
289
+ default: null,
290
+ },
291
+ /** Existing item for edit mode. Pass null for create mode. */
292
+ item: {
293
+ type: Object,
294
+ default: null,
295
+ },
296
+ /** Dialog title. Defaults to "Create {schema.title}" or "Edit {schema.title}". */
297
+ dialogTitle: {
298
+ type: String,
299
+ default: '',
300
+ },
301
+ /** Manual field definitions. Overrides schema-generated fields when provided. */
302
+ fields: {
303
+ type: Array,
304
+ default: null,
305
+ },
306
+ /** Field keys to exclude from auto-generated form */
307
+ excludeFields: {
308
+ type: Array,
309
+ default: () => [],
310
+ },
311
+ /** Field keys to include (whitelist mode) */
312
+ includeFields: {
313
+ type: Array,
314
+ default: null,
315
+ },
316
+ /** Per-field overrides passed to fieldsFromSchema */
317
+ fieldOverrides: {
318
+ type: Object,
319
+ default: () => ({}),
320
+ },
321
+ /** Which field is the "name" (used in result messages) */
322
+ nameField: {
323
+ type: String,
324
+ default: 'title',
325
+ },
326
+ /** NcDialog size */
327
+ size: {
328
+ type: String,
329
+ default: 'normal',
330
+ },
331
+ /** Success message. Defaults to "Item saved successfully." */
332
+ successText: {
333
+ type: String,
334
+ default: '',
335
+ },
336
+ cancelLabel: { type: String, default: 'Cancel' },
337
+ closeLabel: { type: String, default: 'Close' },
338
+ /** Confirm button label. Defaults to "Create" or "Save". */
339
+ confirmLabel: {
340
+ type: String,
341
+ default: '',
342
+ },
343
+ },
344
+
345
+ data() {
346
+ return {
347
+ formData: {},
348
+ errors: {},
349
+ loading: false,
350
+ result: null,
351
+ closeTimeout: null,
352
+ }
353
+ },
354
+
355
+ computed: {
356
+ isCreateMode() {
357
+ return !this.item
358
+ },
359
+
360
+ schemaTitle() {
361
+ return (this.schema && this.schema.title) || 'Item'
362
+ },
363
+
364
+ resolvedTitle() {
365
+ if (this.dialogTitle) return this.dialogTitle
366
+ return this.isCreateMode
367
+ ? `Create ${this.schemaTitle}`
368
+ : `Edit ${this.schemaTitle}`
369
+ },
370
+
371
+ resolvedConfirmLabel() {
372
+ if (this.confirmLabel) return this.confirmLabel
373
+ return this.isCreateMode ? 'Create' : 'Save'
374
+ },
375
+
376
+ resolvedSuccessText() {
377
+ if (this.successText) return this.successText
378
+ return `${this.schemaTitle} saved successfully.`
379
+ },
380
+
381
+ resolvedFields() {
382
+ // Manual fields take priority
383
+ if (this.fields) return this.fields
384
+
385
+ // Auto-generate from schema
386
+ return fieldsFromSchema(this.schema, {
387
+ exclude: this.excludeFields,
388
+ include: this.includeFields,
389
+ overrides: this.fieldOverrides,
390
+ })
391
+ },
392
+ },
393
+
394
+ watch: {
395
+ item: {
396
+ immediate: true,
397
+ handler(newItem) {
398
+ this.initFormData(newItem)
399
+ },
400
+ },
401
+ },
402
+
403
+ methods: {
404
+ initFormData(item) {
405
+ if (item) {
406
+ // Edit mode: clone item data
407
+ this.formData = JSON.parse(JSON.stringify(item))
408
+ } else {
409
+ // Create mode: initialize with field defaults
410
+ const data = {}
411
+ for (const field of this.resolvedFields) {
412
+ if (field.default !== null && field.default !== undefined) {
413
+ data[field.key] = field.default
414
+ } else if (field.widget === 'checkbox') {
415
+ data[field.key] = false
416
+ } else if (field.widget === 'tags' || field.widget === 'multiselect') {
417
+ data[field.key] = []
418
+ } else {
419
+ data[field.key] = null
420
+ }
421
+ }
422
+ this.formData = data
423
+ }
424
+ this.errors = {}
425
+ },
426
+
427
+ updateField(key, value) {
428
+ this.$set(this.formData, key, value)
429
+ // Clear error when field is edited
430
+ if (this.errors[key]) {
431
+ this.$delete(this.errors, key)
432
+ }
433
+ },
434
+
435
+ getEnumOptions(field) {
436
+ if (!field.enum) return []
437
+ return field.enum.map((val) => ({
438
+ id: val,
439
+ label: String(val),
440
+ }))
441
+ },
442
+
443
+ getSelectedEnumOption(field) {
444
+ const val = this.formData[field.key]
445
+ if (val === null || val === undefined) return null
446
+ return { id: val, label: String(val) }
447
+ },
448
+
449
+ onSelectChange(key, option) {
450
+ this.updateField(key, option ? option.id : null)
451
+ },
452
+
453
+ getArrayEnumOptions(field) {
454
+ if (!field.items || !field.items.enum) return []
455
+ return field.items.enum.map((val) => ({
456
+ id: val,
457
+ label: String(val),
458
+ }))
459
+ },
460
+
461
+ getSelectedArrayOptions(field) {
462
+ const val = this.formData[field.key]
463
+ if (!Array.isArray(val)) return []
464
+ return val.map((v) => ({ id: v, label: String(v) }))
465
+ },
466
+
467
+ onMultiSelectChange(key, options) {
468
+ this.updateField(key, (options || []).map((o) => o.id))
469
+ },
470
+
471
+ /**
472
+ * Run client-side validation on all form fields.
473
+ * Checks required, minLength, maxLength, pattern, minimum, maximum.
474
+ *
475
+ * @return {boolean} True if all fields pass validation
476
+ * @public
477
+ */
478
+ validate() {
479
+ const newErrors = {}
480
+ for (const field of this.resolvedFields) {
481
+ const value = this.formData[field.key]
482
+
483
+ // Required check
484
+ if (field.required) {
485
+ if (value === null || value === undefined || value === '') {
486
+ newErrors[field.key] = `${field.label} is required.`
487
+ continue
488
+ }
489
+ if (Array.isArray(value) && value.length === 0) {
490
+ newErrors[field.key] = `${field.label} is required.`
491
+ continue
492
+ }
493
+ }
494
+
495
+ // Skip further validation if empty and not required
496
+ if (value === null || value === undefined || value === '') continue
497
+
498
+ const v = field.validation || {}
499
+
500
+ // String length checks
501
+ if (typeof value === 'string') {
502
+ if (v.minLength !== undefined && value.length < v.minLength) {
503
+ newErrors[field.key] = `Minimum ${v.minLength} characters.`
504
+ } else if (v.maxLength !== undefined && value.length > v.maxLength) {
505
+ newErrors[field.key] = `Maximum ${v.maxLength} characters.`
506
+ } else if (v.pattern !== undefined) {
507
+ try {
508
+ if (!new RegExp(v.pattern).test(value)) {
509
+ newErrors[field.key] = 'Invalid format.'
510
+ }
511
+ } catch {
512
+ // Ignore invalid regex patterns
513
+ }
514
+ }
515
+ }
516
+
517
+ // Number range checks
518
+ if (typeof value === 'number') {
519
+ if (v.minimum !== undefined && value < v.minimum) {
520
+ newErrors[field.key] = `Minimum value is ${v.minimum}.`
521
+ } else if (v.maximum !== undefined && value > v.maximum) {
522
+ newErrors[field.key] = `Maximum value is ${v.maximum}.`
523
+ }
524
+ }
525
+ }
526
+
527
+ this.errors = newErrors
528
+ return Object.keys(newErrors).length === 0
529
+ },
530
+
531
+ executeConfirm() {
532
+ if (!this.validate()) return
533
+
534
+ this.loading = true
535
+ /**
536
+ * @event confirm Emitted when the user confirms the form.
537
+ * Payload: form data object. Includes `id` when editing.
538
+ */
539
+ this.$emit('confirm', { ...this.formData })
540
+ },
541
+
542
+ /**
543
+ * Set the result of the save operation. Call this from the parent
544
+ * after the API call completes.
545
+ *
546
+ * @param {{ success?: boolean, error?: string }} resultData
547
+ * @public
548
+ */
549
+ setResult(resultData) {
550
+ this.loading = false
551
+ this.result = resultData
552
+ if (resultData.success) {
553
+ this.closeTimeout = setTimeout(() => {
554
+ this.$emit('close')
555
+ }, 2000)
556
+ }
557
+ },
558
+
559
+ /**
560
+ * Set per-field validation errors from the server. Call this from
561
+ * the parent when the API returns validation errors.
562
+ *
563
+ * @param {object} fieldErrors Object keyed by field key with error messages
564
+ * @public
565
+ */
566
+ setValidationErrors(fieldErrors) {
567
+ this.loading = false
568
+ this.errors = { ...this.errors, ...fieldErrors }
569
+ },
570
+ },
571
+ }
572
+ </script>
573
+
574
+ <style scoped>
575
+ .cn-form-dialog__form {
576
+ display: flex;
577
+ flex-direction: column;
578
+ gap: 4px;
579
+ }
580
+
581
+ .cn-form-dialog__field {
582
+ margin-bottom: 8px;
583
+ }
584
+
585
+ .cn-form-dialog__textarea-wrapper,
586
+ .cn-form-dialog__select-wrapper {
587
+ display: flex;
588
+ flex-direction: column;
589
+ gap: 4px;
590
+ }
591
+
592
+ .cn-form-dialog__label {
593
+ font-weight: 600;
594
+ font-size: 0.9em;
595
+ color: var(--color-main-text);
596
+ }
597
+
598
+ .cn-form-dialog__textarea {
599
+ width: 100%;
600
+ min-height: 80px;
601
+ padding: 8px;
602
+ border: 2px solid var(--color-border-maxcontrast);
603
+ border-radius: var(--border-radius-large);
604
+ background-color: var(--color-main-background);
605
+ color: var(--color-main-text);
606
+ font-family: inherit;
607
+ font-size: inherit;
608
+ resize: vertical;
609
+ }
610
+
611
+ .cn-form-dialog__textarea:focus {
612
+ border-color: var(--color-primary-element);
613
+ outline: none;
614
+ }
615
+
616
+ .cn-form-dialog__textarea:disabled {
617
+ opacity: 0.5;
618
+ cursor: not-allowed;
619
+ }
620
+
621
+ .cn-form-dialog__helper {
622
+ font-size: 0.85em;
623
+ color: var(--color-text-maxcontrast);
624
+ }
625
+
626
+ .cn-form-dialog__helper--error {
627
+ color: var(--color-error);
628
+ }
629
+ </style>
@@ -0,0 +1 @@
1
+ export { default as CnFormDialog } from './CnFormDialog.vue'