@drax/crud-vue 3.39.0 → 3.47.0

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 (40) hide show
  1. package/package.json +4 -4
  2. package/src/components/Crud.vue +21 -3
  3. package/src/components/CrudActiveFilters.vue +11 -9
  4. package/src/components/CrudAi.vue +38 -25
  5. package/src/components/CrudAutocomplete.vue +14 -2
  6. package/src/components/CrudDialog.vue +6 -6
  7. package/src/components/CrudExportList.vue +13 -12
  8. package/src/components/CrudFieldRange.vue +8 -4
  9. package/src/components/CrudFilters.vue +8 -2
  10. package/src/components/CrudFiltersAction.vue +3 -3
  11. package/src/components/CrudFiltersDynamic.vue +18 -10
  12. package/src/components/CrudForm.vue +44 -31
  13. package/src/components/CrudFormField.vue +48 -8
  14. package/src/components/CrudFormList.vue +44 -32
  15. package/src/components/CrudFormRecord.vue +19 -11
  16. package/src/components/CrudImportList.vue +17 -16
  17. package/src/components/CrudList.vue +41 -12
  18. package/src/components/CrudListGallery.vue +65 -27
  19. package/src/components/CrudListTable.vue +49 -10
  20. package/src/components/CrudNotify.vue +3 -1
  21. package/src/components/CrudRefDisplay.vue +22 -23
  22. package/src/components/CrudRouteForm.vue +8 -4
  23. package/src/components/CrudRowValue.vue +9 -8
  24. package/src/components/CrudSearch.vue +2 -1
  25. package/src/components/buttons/CrudAiButton.vue +3 -2
  26. package/src/components/buttons/CrudColumnsButton.vue +17 -7
  27. package/src/components/buttons/CrudCreateButton.vue +3 -2
  28. package/src/components/buttons/CrudCreateOnTheFlyButton.vue +7 -2
  29. package/src/components/buttons/CrudDeleteButton.vue +3 -2
  30. package/src/components/buttons/CrudExportButton.vue +7 -6
  31. package/src/components/buttons/CrudFilterButton.vue +4 -3
  32. package/src/components/buttons/CrudGroupByButton.vue +28 -15
  33. package/src/components/buttons/CrudImportButton.vue +10 -8
  34. package/src/components/buttons/CrudRefreshButton.vue +3 -2
  35. package/src/components/buttons/CrudSavedQueriesButton.vue +37 -19
  36. package/src/components/buttons/CrudUpdateButton.vue +3 -2
  37. package/src/components/buttons/CrudViewButton.vue +3 -2
  38. package/src/components/combobox/EntityCombobox.vue +4 -0
  39. package/src/composables/useCrudRefDisplay.ts +28 -5
  40. package/src/index.ts +4 -0
@@ -168,22 +168,24 @@ const onlyView = computed(()=> {
168
168
  </script>
169
169
 
170
170
  <template>
171
- <v-form ref="formRef" @submit.prevent>
172
- <v-card flat>
171
+ <v-form id="crud-form" class="crud-form" ref="formRef" @submit.prevent>
172
+ <v-card id="crud-form-card" class="crud-form__card" flat>
173
173
 
174
- <v-card-subtitle v-if="getItemId(form)">ID: {{ getItemId(form) }}</v-card-subtitle>
174
+ <v-card-subtitle v-if="getItemId(form)" id="crud-form-id" class="crud-form__id">ID: {{ getItemId(form) }}</v-card-subtitle>
175
175
 
176
- <v-card-text v-if="error">
177
- <v-alert color="error">{{ te(error) ? t(error) : error }}</v-alert>
176
+ <v-card-text v-if="error" id="crud-form-error-content" class="crud-form__error-content">
177
+ <v-alert id="crud-form-error-alert" class="crud-form__error-alert" color="error">{{ te(error) ? t(error) : error }}</v-alert>
178
178
  </v-card-text>
179
179
 
180
- <v-card-text>
180
+ <v-card-text id="crud-form-content" class="crud-form__content">
181
181
 
182
182
  <!--GENERAL-->
183
- <v-row>
183
+ <v-row id="crud-form-general-fields" class="crud-form__general-fields">
184
184
  <v-col
185
185
  v-for="field in generalFields"
186
186
  :key="field.name"
187
+ :id="`crud-form-general-field-column-${field.name}`"
188
+ class="crud-form__general-field-column"
187
189
  :cols="field.cols ? field.cols : 12"
188
190
  :sm="field.sm ? field.sm : undefined"
189
191
  :md="field.md ? field.md : undefined"
@@ -201,6 +203,8 @@ const onlyView = computed(()=> {
201
203
  >
202
204
 
203
205
  <crud-form-field
206
+ :id="`crud-form-general-field-${field.name}`"
207
+ class="crud-form__general-field"
204
208
  :field="field"
205
209
  :entity="entity"
206
210
  v-model="form[field.name]"
@@ -226,22 +230,24 @@ const onlyView = computed(()=> {
226
230
  </v-row>
227
231
 
228
232
  <!--TAB-->
229
- <v-card class="mt-4" variant="outlined" v-if="entity.tabs && entity.tabs.length > 0">
233
+ <v-card id="crud-form-tabs-card" class="crud-form__tabs-card mt-4" variant="outlined" v-if="entity.tabs && entity.tabs.length > 0">
230
234
 
231
- <v-tabs v-model="tabSelected">
232
- <v-tab v-for="tab in entity.tabs" :value="tab" :key="tab">
233
- <span :class="tabInputErrors(tab) ? 'text-red' : ''">
235
+ <v-tabs id="crud-form-tabs" class="crud-form__tabs" v-model="tabSelected">
236
+ <v-tab v-for="tab in entity.tabs" :id="`crud-form-tab-${tab}`" class="crud-form__tab" :value="tab" :key="tab">
237
+ <span :id="`crud-form-tab-label-${tab}`" :class="['crud-form__tab-label', tabInputErrors(tab) ? 'text-red' : '']">
234
238
  {{ te(tab) ? t(tab) : tab }}
235
239
  </span>
236
240
  </v-tab>
237
241
  </v-tabs>
238
- <v-card-text>
239
- <v-tabs-window v-model="tabSelected">
240
- <v-tabs-window-item v-for="tab in entity.tabs" :value="tab">
241
- <v-row class="pt-3">
242
+ <v-card-text id="crud-form-tabs-content" class="crud-form__tabs-content">
243
+ <v-tabs-window id="crud-form-tabs-window" class="crud-form__tabs-window" v-model="tabSelected">
244
+ <v-tabs-window-item v-for="tab in entity.tabs" :id="`crud-form-tab-panel-${tab}`" class="crud-form__tab-panel" :value="tab">
245
+ <v-row class="crud-form__tab-fields pt-3">
242
246
  <v-col
243
247
  v-for="field in tabFields(tab)"
244
248
  :key="field.name"
249
+ :id="`crud-form-tab-field-column-${tab}-${field.name}`"
250
+ class="crud-form__tab-field-column"
245
251
  :cols="field.cols ? field.cols : 12"
246
252
  :sm="field.sm ? field.sm : undefined"
247
253
  :md="field.md ? field.md : undefined"
@@ -261,6 +267,8 @@ const onlyView = computed(()=> {
261
267
 
262
268
 
263
269
  <crud-form-field
270
+ :id="`crud-form-tab-field-${tab}-${field.name}`"
271
+ class="crud-form__tab-field"
264
272
  :field="field"
265
273
  :entity="entity"
266
274
  v-model="form[field.name]"
@@ -288,14 +296,14 @@ const onlyView = computed(()=> {
288
296
  </v-card>
289
297
 
290
298
  <!--MENU-->
291
- <v-row v-if="entity.menus && entity.menus.length > 0">
292
- <v-col cols="12" sm="4" md="3">
293
- <v-card variant="outlined">
294
- <v-card-text :style="{ maxHeight: menuMaxHeight, overflowY: 'auto' }">
295
- <v-list v-model="menuSelected">
296
- <v-list-item v-for="menu in entity.menus" rounded="shaped" :value="menu" @click="menuSelected = menu">
297
- <v-list-item-title>
298
- <span :class="menuInputErrors(menu) ? 'text-red' : ''">
299
+ <v-row v-if="entity.menus && entity.menus.length > 0" id="crud-form-menu-layout" class="crud-form__menu-layout">
300
+ <v-col id="crud-form-menu-nav-column" class="crud-form__menu-nav-column" cols="12" sm="4" md="3">
301
+ <v-card id="crud-form-menu-nav-card" class="crud-form__menu-nav-card" variant="outlined">
302
+ <v-card-text id="crud-form-menu-nav-content" class="crud-form__menu-nav-content" :style="{ maxHeight: menuMaxHeight, overflowY: 'auto' }">
303
+ <v-list id="crud-form-menu-list" class="crud-form__menu-list" v-model="menuSelected">
304
+ <v-list-item v-for="menu in entity.menus" :id="`crud-form-menu-item-${menu}`" class="crud-form__menu-item" rounded="shaped" :value="menu" @click="menuSelected = menu">
305
+ <v-list-item-title class="crud-form__menu-item-title">
306
+ <span :id="`crud-form-menu-label-${menu}`" :class="['crud-form__menu-label', menuInputErrors(menu) ? 'text-red' : '']">
299
307
  {{ te(menu) ? t(menu) : menu }}
300
308
  </span>
301
309
 
@@ -305,13 +313,15 @@ const onlyView = computed(()=> {
305
313
  </v-card-text>
306
314
  </v-card>
307
315
  </v-col>
308
- <v-col cols="12" sm="8" md="9">
309
- <v-card v-if="menuSelected" variant="outlined">
310
- <v-card-text>
311
- <v-row>
316
+ <v-col id="crud-form-menu-fields-column" class="crud-form__menu-fields-column" cols="12" sm="8" md="9">
317
+ <v-card v-if="menuSelected" id="crud-form-menu-fields-card" class="crud-form__menu-fields-card" variant="outlined">
318
+ <v-card-text id="crud-form-menu-fields-content" class="crud-form__menu-fields-content">
319
+ <v-row id="crud-form-menu-fields" class="crud-form__menu-fields">
312
320
  <v-col
313
321
  v-for="field in menuFields(menuSelected)"
314
322
  :key="field.name"
323
+ :id="`crud-form-menu-field-column-${menuSelected}-${field.name}`"
324
+ class="crud-form__menu-field-column"
315
325
  :cols="field.cols ? field.cols : 12"
316
326
  :sm="field.sm ? field.sm : undefined"
317
327
  :md="field.md ? field.md : undefined"
@@ -328,6 +338,8 @@ const onlyView = computed(()=> {
328
338
  }"
329
339
  >
330
340
  <crud-form-field
341
+ :id="`crud-form-menu-field-${menuSelected}-${field.name}`"
342
+ class="crud-form__menu-field"
331
343
  :field="field"
332
344
  :entity="entity"
333
345
  v-model="form[field.name]"
@@ -356,18 +368,19 @@ const onlyView = computed(()=> {
356
368
 
357
369
  </v-card-text>
358
370
 
359
- <v-card-actions>
371
+ <v-card-actions id="crud-form-actions" class="crud-form__actions">
360
372
  <v-spacer></v-spacer>
361
- <v-btn variant="text" :class="entity.cancelBtnFormClass" @click="cancel">
373
+ <v-btn id="crud-form-cancel-button" variant="text" :class="['crud-form__cancel-button', entity.cancelBtnFormClass]" @click="cancel">
362
374
  {{ operation == 'view' ? t('action.close') : t('action.cancel') }}
363
375
  </v-btn>
364
- <v-btn variant="flat" v-if="operation != 'view'" :class="entity.submitBtnFormClass" @click="submit" :loading="store.loading">
376
+ <v-btn id="crud-form-submit-button" variant="flat" v-if="operation != 'view'" :class="['crud-form__submit-button', entity.submitBtnFormClass]" @click="submit" :loading="store.loading">
365
377
  {{ operation ? t('action.'+operation) : t('action.sent') }}
366
378
  </v-btn>
367
379
  <v-btn
380
+ id="crud-form-submit-and-return-button"
368
381
  variant="flat"
369
382
  v-if="showSubmitAndReturn && ['create', 'edit'].includes(operation || '')"
370
- :class="entity.submitBtnFormClass"
383
+ :class="['crud-form__submit-and-return-button', entity.submitBtnFormClass]"
371
384
  @click="submitAndReturn"
372
385
  :loading="store.loading"
373
386
  >
@@ -95,9 +95,11 @@ const hasHideDetails = computed(() => {
95
95
 
96
96
  <template>
97
97
 
98
- <div v-if="field && field.type && (!field.permission || hasPermission(field.permission) )">
98
+ <div v-if="field && field.type && (!field.permission || hasPermission(field.permission) )" :id="`crud-form-field-${name}`" :class="['crud-form-field', `crud-form-field--${field.type.replace(/\./g, '-')}`]">
99
99
  <v-text-field
100
100
  v-if="field.type === 'string'"
101
+ :id="`crud-form-field-string-${name}`"
102
+ class="crud-form-field__string-input"
101
103
  type="text"
102
104
  :name="name"
103
105
  :label="label"
@@ -124,6 +126,8 @@ const hasHideDetails = computed(() => {
124
126
 
125
127
  <v-textarea
126
128
  v-if="field.type === 'longString'"
129
+ :id="`crud-form-field-long-string-${name}`"
130
+ class="crud-form-field__long-string-input"
127
131
  :rows="field.rows || 5"
128
132
  type="text"
129
133
  :name="name"
@@ -151,6 +155,8 @@ const hasHideDetails = computed(() => {
151
155
 
152
156
  <v-text-field
153
157
  v-if="field.type === 'password'"
158
+ :id="`crud-form-field-password-${name}`"
159
+ class="crud-form-field__password-input"
154
160
  :name="name"
155
161
  :label="label"
156
162
  :hint="hint ?? field.hint"
@@ -179,6 +185,8 @@ const hasHideDetails = computed(() => {
179
185
 
180
186
  <v-combobox
181
187
  v-if="field.type === 'enum' && !field.noFilter"
188
+ :id="`crud-form-field-enum-combobox-${name}`"
189
+ class="crud-form-field__enum-combobox"
182
190
  :name="name"
183
191
  :label="label"
184
192
  :hint="hint ?? field.hint"
@@ -211,6 +219,8 @@ const hasHideDetails = computed(() => {
211
219
 
212
220
  <v-select
213
221
  v-if="field.type === 'select'"
222
+ :id="`crud-form-field-select-${name}`"
223
+ class="crud-form-field__select"
214
224
  :name="name"
215
225
  :label="label"
216
226
  :hint="hint ?? field.hint"
@@ -244,6 +254,8 @@ const hasHideDetails = computed(() => {
244
254
  <template v-slot:item="{ props: itemProps, item }">
245
255
  <v-list-item
246
256
  v-bind="itemProps"
257
+ :id="`crud-form-field-select-item-${name}-${item.raw.value}`"
258
+ class="crud-form-field__select-item"
247
259
  density="compact"
248
260
  :title="item.raw.title"
249
261
  :color="item.raw.color"
@@ -253,7 +265,7 @@ const hasHideDetails = computed(() => {
253
265
  </template>
254
266
 
255
267
  <template v-slot:selection="{item}">
256
- <v-chip tile density="compact" :color="item.raw.color" :prepend-icon="item.raw.icon">{{
268
+ <v-chip tile density="compact" :id="`crud-form-field-select-selection-${name}-${item.raw.value}`" class="crud-form-field__select-selection-chip" :color="item.raw.color" :prepend-icon="item.raw.icon">{{
257
269
  item.raw.title
258
270
  }}
259
271
  </v-chip>
@@ -263,6 +275,8 @@ const hasHideDetails = computed(() => {
263
275
 
264
276
  <v-select
265
277
  v-if="field.type === 'enum' && field.noFilter"
278
+ :id="`crud-form-field-enum-select-${name}`"
279
+ class="crud-form-field__enum-select"
266
280
  :name="name"
267
281
  :label="label"
268
282
  :hint="hint ?? field.hint"
@@ -291,6 +305,8 @@ const hasHideDetails = computed(() => {
291
305
 
292
306
  <v-text-field
293
307
  v-if="field.type === 'number'"
308
+ :id="`crud-form-field-number-${name}`"
309
+ class="crud-form-field__number-input"
294
310
  type="number"
295
311
  :name="name"
296
312
  :label="label"
@@ -317,6 +333,8 @@ const hasHideDetails = computed(() => {
317
333
 
318
334
  <media-field
319
335
  v-if="field.type === 'file'"
336
+ :id="`crud-form-field-file-${name}`"
337
+ class="crud-form-field__file-input"
320
338
  :name="name"
321
339
  :label="label"
322
340
  v-model.number="valueModel"
@@ -339,6 +357,8 @@ const hasHideDetails = computed(() => {
339
357
 
340
358
  <media-full-field
341
359
  v-if="field.type === 'fullFile'"
360
+ :id="`crud-form-field-full-file-${name}`"
361
+ class="crud-form-field__full-file-input"
342
362
  :name="name"
343
363
  :label="label"
344
364
  v-model.number="valueModel"
@@ -361,6 +381,8 @@ const hasHideDetails = computed(() => {
361
381
 
362
382
  <v-switch
363
383
  v-if="field.type === 'boolean'"
384
+ :id="`crud-form-field-boolean-${name}`"
385
+ class="crud-form-field__boolean-switch"
364
386
  :name="name"
365
387
  :label="label"
366
388
  :hint="hint ?? field.hint"
@@ -388,6 +410,8 @@ const hasHideDetails = computed(() => {
388
410
 
389
411
  <v-date-input
390
412
  v-if="field.type === 'date'"
413
+ :id="`crud-form-field-date-${name}`"
414
+ class="crud-form-field__date-input"
391
415
  :name="name"
392
416
  :label="label"
393
417
  :hint="hint ?? field.hint"
@@ -422,12 +446,14 @@ const hasHideDetails = computed(() => {
422
446
  @input="onInput"
423
447
  >
424
448
  <template v-if="field.endOfDay && field.showEndOfDayChip !== false" v-slot:append-inner>
425
- <v-chip size="small">23:59</v-chip>
449
+ <v-chip :id="`crud-form-field-date-end-of-day-${name}`" class="crud-form-field__end-of-day-chip" size="small">23:59</v-chip>
426
450
  </template>
427
451
  </v-date-input>
428
452
 
429
453
  <crud-autocomplete
430
454
  v-if="field.type === 'ref'"
455
+ :id="`crud-form-field-ref-${name}`"
456
+ class="crud-form-field__ref-autocomplete"
431
457
  :entity="entity.getRef(field.ref)"
432
458
  :field="field"
433
459
  v-model="valueModel"
@@ -455,14 +481,16 @@ const hasHideDetails = computed(() => {
455
481
  :on-input="onInput"
456
482
  />
457
483
 
458
- <v-card v-if="field.type === 'object'" class="mt-3" variant="flat" border>
484
+ <v-card v-if="field.type === 'object'" :id="`crud-form-field-object-${name}`" class="crud-form-field__object-card mt-3" variant="flat" border>
459
485
 
460
- <v-card-title class="text-h5">{{ field.label }}</v-card-title>
461
- <v-card-text>
486
+ <v-card-title :id="`crud-form-field-object-title-${name}`" class="crud-form-field__object-title text-h5">{{ field.label }}</v-card-title>
487
+ <v-card-text :id="`crud-form-field-object-content-${name}`" class="crud-form-field__object-content">
462
488
 
463
- <v-row dense>
464
- <v-col cols="12" v-for="oField in field.objectFields">
489
+ <v-row :id="`crud-form-field-object-row-${name}`" class="crud-form-field__object-row" dense>
490
+ <v-col :id="`crud-form-field-object-column-${name}-${oField.name}`" class="crud-form-field__object-column" cols="12" v-for="oField in field.objectFields">
465
491
  <crud-form-field
492
+ :id="`crud-form-field-object-field-${name}-${oField.name}`"
493
+ class="crud-form-field__object-field"
466
494
 
467
495
  :entity="entity"
468
496
  :field="oField"
@@ -490,6 +518,8 @@ const hasHideDetails = computed(() => {
490
518
 
491
519
  <v-combobox
492
520
  v-if="field.type === 'array.string'"
521
+ :id="`crud-form-field-array-string-${name}`"
522
+ class="crud-form-field__array-string-combobox"
493
523
  type="text"
494
524
  :name="name"
495
525
  :label="label"
@@ -525,6 +555,8 @@ const hasHideDetails = computed(() => {
525
555
 
526
556
  <v-combobox
527
557
  v-if="field.type === 'array.enum'"
558
+ :id="`crud-form-field-array-enum-${name}`"
559
+ class="crud-form-field__array-enum-combobox"
528
560
  type="text"
529
561
  :name="name"
530
562
  :label="label"
@@ -562,6 +594,8 @@ const hasHideDetails = computed(() => {
562
594
 
563
595
  <crud-autocomplete
564
596
  v-if="field.type === 'array.ref'"
597
+ :id="`crud-form-field-array-ref-${name}`"
598
+ class="crud-form-field__array-ref-autocomplete"
565
599
  :entity="entity.getRef(field.ref)"
566
600
  :field="field"
567
601
  :item-title="field?.refDisplay"
@@ -593,6 +627,8 @@ const hasHideDetails = computed(() => {
593
627
 
594
628
  <v-combobox
595
629
  v-if="field.type === 'array.number'"
630
+ :id="`crud-form-field-array-number-${name}`"
631
+ class="crud-form-field__array-number-combobox"
596
632
  type="number"
597
633
  :name="name"
598
634
  :label="label"
@@ -628,6 +664,8 @@ const hasHideDetails = computed(() => {
628
664
 
629
665
  <crud-form-list
630
666
  v-if="field.type === 'array.object'"
667
+ :id="`crud-form-field-array-object-${name}`"
668
+ class="crud-form-field__array-object-list"
631
669
  :entity="entity"
632
670
  :field="field"
633
671
  v-model="valueModel"
@@ -642,6 +680,8 @@ const hasHideDetails = computed(() => {
642
680
 
643
681
  <crud-form-record
644
682
  v-if="field.type === 'record'"
683
+ :id="`crud-form-field-record-${name}`"
684
+ class="crud-form-field__record"
645
685
  :entity="entity"
646
686
  :field="field"
647
687
  v-model="valueModel"
@@ -172,15 +172,16 @@ function clearDragState() {
172
172
  </script>
173
173
 
174
174
  <template>
175
- <v-card class="mt-3" variant="flat" border>
175
+ <v-card :id="`crud-form-list-${field.name}`" class="crud-form-list mt-3" variant="flat" border>
176
176
 
177
- <v-card-title class="text-h5">{{ label }}</v-card-title>
177
+ <v-card-title :id="`crud-form-list-title-${field.name}`" class="crud-form-list__title text-h5">{{ label }}</v-card-title>
178
178
 
179
179
  <!--ACCORDION-->
180
- <v-card-text v-if="field.arrayObjectUI === 'accordion' || xs">
181
- <v-expansion-panels>
180
+ <v-card-text v-if="field.arrayObjectUI === 'accordion' || xs" :id="`crud-form-list-accordion-${field.name}`" class="crud-form-list__accordion">
181
+ <v-expansion-panels :id="`crud-form-list-accordion-panels-${field.name}`" class="crud-form-list__accordion-panels">
182
182
  <v-expansion-panel v-for="(item,index) in valueModel" :key="index"
183
- :class="{'crud-form-list--drag-over': dragOverIndex === index}"
183
+ :id="`crud-form-list-accordion-item-${field.name}-${index}`"
184
+ :class="['crud-form-list__accordion-item', {'crud-form-list--drag-over': dragOverIndex === index}]"
184
185
  :draggable="isSortable"
185
186
  @dragstart="onDragStart(index)"
186
187
  @dragenter.prevent="onDragEnter(index)"
@@ -188,28 +189,31 @@ function clearDragState() {
188
189
  @drop.prevent="onDrop(index)"
189
190
  @dragend="clearDragState">
190
191
 
191
- <v-expansion-panel-title>
192
- <v-icon v-if="isSortable" class="mr-2" size="small">mdi-drag</v-icon>
193
- <v-chip class="mr-2" :color="hasError(index) ? 'red':'teal'">{{ index }}</v-chip>
192
+ <v-expansion-panel-title class="crud-form-list__accordion-title">
193
+ <v-icon v-if="isSortable" class="crud-form-list__drag-icon mr-2" size="small">mdi-drag</v-icon>
194
+ <v-chip class="crud-form-list__index-chip mr-2" :color="hasError(index) ? 'red':'teal'">{{ index }}</v-chip>
194
195
  {{ getItemTitle(index) }}
195
196
 
196
197
  <template v-slot:actions="{expanded}">
197
- <v-icon>{{ expanded ? "mdi-menu-down" : "mdi-menu-up" }}</v-icon>
198
+ <v-icon class="crud-form-list__expand-icon">{{ expanded ? "mdi-menu-down" : "mdi-menu-up" }}</v-icon>
198
199
 
199
200
  <v-btn v-if="!readonly" variant="text" @click="removeItem(index)" density="compact"
200
- class="text-red text--darken-3">
201
+ :id="`crud-form-list-accordion-remove-${field.name}-${index}`"
202
+ class="crud-form-list__remove-button text-red text--darken-3">
201
203
  <v-icon>mdi-close</v-icon>
202
204
  </v-btn>
203
205
  </template>
204
206
 
205
207
  </v-expansion-panel-title>
206
208
 
207
- <v-expansion-panel-text>
208
- <v-row>
209
+ <v-expansion-panel-text class="crud-form-list__accordion-content">
210
+ <v-row class="crud-form-list__fields-row">
209
211
  <template v-for="key in Object.keys(item as Record<string, any>)" :key="key">
210
- <v-col cols="12">
212
+ <v-col :id="`crud-form-list-accordion-field-column-${field.name}-${index}-${key}`" class="crud-form-list__field-column" cols="12">
211
213
  <crud-form-field
212
214
  v-if="hasField(key)"
215
+ :id="`crud-form-list-accordion-field-${field.name}-${index}-${key}`"
216
+ class="crud-form-list__field"
213
217
  :entity="entity"
214
218
  :field="getField(key)"
215
219
  v-model="(valueModel[index] as any)[key]"
@@ -230,7 +234,7 @@ function clearDragState() {
230
234
  </v-expansion-panel-text>
231
235
 
232
236
  </v-expansion-panel>
233
- <v-btn icon @click="addItem" class="text-blue text--darken-3">
237
+ <v-btn :id="`crud-form-list-accordion-add-${field.name}`" icon @click="addItem" class="crud-form-list__add-button text-blue text--darken-3">
234
238
  <v-icon>mdi-plus</v-icon>
235
239
  </v-btn>
236
240
 
@@ -238,28 +242,29 @@ function clearDragState() {
238
242
  </v-card-text>
239
243
 
240
244
  <!--CHIPS-->
241
- <v-card-text v-else-if="field.arrayObjectUI === 'chips' ">
242
- <v-row dense>
245
+ <v-card-text v-else-if="field.arrayObjectUI === 'chips' " :id="`crud-form-list-chips-${field.name}`" class="crud-form-list__chips">
246
+ <v-row class="crud-form-list__chips-row" dense>
243
247
  <v-col cols="12" sm="12" md="12">
244
248
  <v-card variant="flat">
245
249
  <v-card-text>
246
- <v-btn color="primary" rounded="xl" @click="addItem"
247
- class="text-blue text--darken-3 float-left mt-1 mr-2">
250
+ <v-btn :id="`crud-form-list-chips-add-${field.name}`" color="primary" rounded="xl" @click="addItem"
251
+ class="crud-form-list__chips-add-button text-blue text--darken-3 float-left mt-1 mr-2">
248
252
  <v-icon>mdi-plus</v-icon>
249
253
  {{ label }}
250
254
  </v-btn>
251
255
 
252
256
 
253
- <v-chip-group v-model="indexSelected"
257
+ <v-chip-group :id="`crud-form-list-chip-group-${field.name}`" class="crud-form-list__chip-group" v-model="indexSelected"
254
258
  :style="{ maxHeight: menuMaxHeight, overflowY: 'auto' }"
255
259
  direction="horizontal"
256
260
 
257
261
  >
258
262
 
259
263
  <v-chip v-for="(item,index) in valueModel" :key="index"
264
+ :id="`crud-form-list-chip-${field.name}-${index}`"
260
265
  :value="index" @click="menuSelect(item, index)"
261
266
  :draggable="isSortable"
262
- :class="{'crud-form-list--drag-over': dragOverIndex === index}"
267
+ :class="['crud-form-list__chip', {'crud-form-list--drag-over': dragOverIndex === index}]"
263
268
  @dragstart="onDragStart(index)"
264
269
  @dragenter.prevent="onDragEnter(index)"
265
270
  @dragover.prevent
@@ -270,7 +275,7 @@ function clearDragState() {
270
275
  {{ getItemTitle(index) || (index) }}
271
276
 
272
277
  <template v-slot:append>
273
- <v-btn variant="text" class="ml-2" density="compact"
278
+ <v-btn variant="text" :id="`crud-form-list-chip-remove-${field.name}-${index}`" class="crud-form-list__chip-remove-button ml-2" density="compact"
274
279
  icon="mdi-close-circle"
275
280
  @click="removeItem(index)"
276
281
  ></v-btn>
@@ -283,13 +288,15 @@ function clearDragState() {
283
288
  </v-card>
284
289
  </v-col>
285
290
  <v-col cols="12" sm="12" md="12">
286
- <v-card v-if="itemSelected" variant="flat">
291
+ <v-card v-if="itemSelected" :id="`crud-form-list-chips-fields-card-${field.name}`" class="crud-form-list__chips-fields-card" variant="flat">
287
292
  <v-card-text>
288
293
  <v-row>
289
294
  <template v-for="key in Object.keys(itemSelected as Record<string, any>)" :key="key">
290
- <v-col cols="12">
295
+ <v-col :id="`crud-form-list-chips-field-column-${field.name}-${key}`" class="crud-form-list__field-column" cols="12">
291
296
  <crud-form-field
292
297
  v-if="hasField(key)"
298
+ :id="`crud-form-list-chips-field-${field.name}-${key}`"
299
+ class="crud-form-list__field"
293
300
  :entity="entity"
294
301
  :field="getField(key)"
295
302
  v-model="(itemSelected as any)[key]"
@@ -313,14 +320,15 @@ function clearDragState() {
313
320
  </v-card-text>
314
321
 
315
322
  <!--MENU-->
316
- <v-card-text v-else>
317
- <v-row>
323
+ <v-card-text v-else :id="`crud-form-list-menu-${field.name}`" class="crud-form-list__menu">
324
+ <v-row class="crud-form-list__menu-row">
318
325
  <v-col cols="12" sm="4" md="3">
319
326
  <v-card variant="outlined">
320
327
  <v-card-text>
321
- <v-list v-model="itemSelected" :style="{ maxHeight: menuMaxHeight, overflowY: 'auto' }">
328
+ <v-list :id="`crud-form-list-menu-list-${field.name}`" class="crud-form-list__menu-list" v-model="itemSelected" :style="{ maxHeight: menuMaxHeight, overflowY: 'auto' }">
322
329
  <v-list-item v-for="(item,index) in valueModel" :key="index" rounded="shaped"
323
- :class="{'crud-form-list--drag-over': dragOverIndex === index}"
330
+ :id="`crud-form-list-menu-item-${field.name}-${index}`"
331
+ :class="['crud-form-list__menu-item', {'crud-form-list--drag-over': dragOverIndex === index}]"
324
332
  :value="item" @click="menuSelect(item, index)"
325
333
  :draggable="isSortable"
326
334
  @dragstart="onDragStart(index)"
@@ -330,34 +338,38 @@ function clearDragState() {
330
338
  @dragend="clearDragState"
331
339
  >
332
340
  <template v-slot:append>
333
- <v-icon v-if="isSortable" size="small" class="mr-2">mdi-drag</v-icon>
341
+ <v-icon v-if="isSortable" size="small" class="crud-form-list__drag-icon mr-2">mdi-drag</v-icon>
334
342
  <v-btn size="x-small" variant="text" color="red" icon="mdi-delete"
343
+ :id="`crud-form-list-menu-remove-${field.name}-${index}`"
344
+ class="crud-form-list__remove-button"
335
345
  @click="removeItem(index)"
336
346
 
337
347
  />
338
348
  </template>
339
349
  <v-list-item-title>
340
- <v-chip class="mr-2" :color="hasError(index) ? 'red':'teal'">{{ index }}</v-chip>
350
+ <v-chip class="crud-form-list__index-chip mr-2" :color="hasError(index) ? 'red':'teal'">{{ index }}</v-chip>
341
351
  {{ getItemTitle(index) }}
342
352
  </v-list-item-title>
343
353
  </v-list-item>
344
354
 
345
355
  </v-list>
346
356
 
347
- <v-btn variant="text" @click="addItem" class="text-blue text--darken-3 float-right my-2">
357
+ <v-btn :id="`crud-form-list-menu-add-${field.name}`" variant="text" @click="addItem" class="crud-form-list__add-button text-blue text--darken-3 float-right my-2">
348
358
  <v-icon>mdi-plus</v-icon>
349
359
  </v-btn>
350
360
  </v-card-text>
351
361
  </v-card>
352
362
  </v-col>
353
363
  <v-col cols="12" sm="8" md="9">
354
- <v-card v-if="itemSelected" variant="outlined">
364
+ <v-card v-if="itemSelected" :id="`crud-form-list-menu-fields-card-${field.name}`" class="crud-form-list__menu-fields-card" variant="outlined">
355
365
  <v-card-text>
356
366
  <v-row>
357
367
  <template v-for="key in Object.keys(itemSelected as Record<string, any>)" :key="key">
358
- <v-col cols="12">
368
+ <v-col :id="`crud-form-list-menu-field-column-${field.name}-${key}`" class="crud-form-list__field-column" cols="12">
359
369
  <crud-form-field
360
370
  v-if="hasField(key)"
371
+ :id="`crud-form-list-menu-field-${field.name}-${key}`"
372
+ class="crud-form-list__field"
361
373
  :entity="entity"
362
374
  :field="getField(key)"
363
375
  v-model="(itemSelected as any)[key]"
@@ -90,18 +90,20 @@ defineEmits(['updateValue'])
90
90
  </script>
91
91
 
92
92
  <template>
93
- <div class="record-field">
94
- <v-card variant="flat" border class="mt-3">
95
- <v-card-title class="text-h5">{{ field.label }}</v-card-title>
96
- <v-card-text>
97
- <div v-if="localEntries.length === 0" class="text-center py-4">
98
- <p class="text-grey">{{ t('common.noData') || 'No data' }}</p>
93
+ <div :id="`crud-form-record-${field.name}`" class="crud-form-record record-field">
94
+ <v-card :id="`crud-form-record-card-${field.name}`" variant="flat" border class="crud-form-record__card mt-3">
95
+ <v-card-title :id="`crud-form-record-title-${field.name}`" class="crud-form-record__title text-h5">{{ field.label }}</v-card-title>
96
+ <v-card-text :id="`crud-form-record-content-${field.name}`" class="crud-form-record__content">
97
+ <div v-if="localEntries.length === 0" :id="`crud-form-record-empty-${field.name}`" class="crud-form-record__empty text-center py-4">
98
+ <p class="crud-form-record__empty-text text-grey">{{ t('common.noData') || 'No data' }}</p>
99
99
  </div>
100
100
 
101
- <div v-for="entry in localEntries" :key="entry.id" class="record-entry mb-3">
102
- <v-row dense>
103
- <v-col cols="12" sm="5">
101
+ <div v-for="entry in localEntries" :key="entry.id" :id="`crud-form-record-entry-${field.name}-${entry.id}`" class="crud-form-record__entry record-entry mb-3">
102
+ <v-row :id="`crud-form-record-entry-row-${field.name}-${entry.id}`" class="crud-form-record__entry-row" dense>
103
+ <v-col :id="`crud-form-record-key-column-${field.name}-${entry.id}`" class="crud-form-record__key-column" cols="12" sm="5">
104
104
  <v-text-field
105
+ :id="`crud-form-record-key-field-${field.name}-${entry.id}`"
106
+ class="crud-form-record__key-field"
105
107
  :model-value="entry.key"
106
108
  :label="t('common.key') || 'Key'"
107
109
  :density="density"
@@ -112,8 +114,10 @@ defineEmits(['updateValue'])
112
114
  outlined
113
115
  />
114
116
  </v-col>
115
- <v-col cols="12" sm="5">
117
+ <v-col :id="`crud-form-record-value-column-${field.name}-${entry.id}`" class="crud-form-record__value-column" cols="12" sm="5">
116
118
  <v-text-field
119
+ :id="`crud-form-record-value-field-${field.name}-${entry.id}`"
120
+ class="crud-form-record__value-field"
117
121
  :model-value="entry.value"
118
122
  :label="t('common.value') || 'Value'"
119
123
  :density="density"
@@ -124,9 +128,11 @@ defineEmits(['updateValue'])
124
128
  outlined
125
129
  />
126
130
  </v-col>
127
- <v-col cols="12" sm="2" class="d-flex align-center">
131
+ <v-col :id="`crud-form-record-actions-column-${field.name}-${entry.id}`" cols="12" sm="2" class="crud-form-record__actions-column d-flex align-center">
128
132
  <v-btn
129
133
  v-if="!readonly"
134
+ :id="`crud-form-record-delete-button-${field.name}-${entry.id}`"
135
+ class="crud-form-record__delete-button"
130
136
  icon="mdi-delete"
131
137
  size="small"
132
138
  color="error"
@@ -141,6 +147,8 @@ defineEmits(['updateValue'])
141
147
 
142
148
  <v-btn
143
149
  v-if="!readonly"
150
+ :id="`crud-form-record-add-button-${field.name}`"
151
+ class="crud-form-record__add-button"
144
152
  prepend-icon="mdi-plus"
145
153
  color="primary"
146
154
  variant="tonal"