@drax/crud-vue 0.51.0 → 1.4.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.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.51.0",
6
+ "version": "1.4.0",
7
7
  "type": "module",
8
8
  "main": "./src/index.ts",
9
9
  "module": "./src/index.ts",
@@ -24,10 +24,10 @@
24
24
  "format": "prettier --write src/"
25
25
  },
26
26
  "dependencies": {
27
- "@drax/common-front": "^0.51.0",
28
- "@drax/crud-front": "^0.51.0",
29
- "@drax/crud-share": "^0.51.0",
30
- "@drax/media-vue": "^0.51.0"
27
+ "@drax/common-front": "^1.4.0",
28
+ "@drax/crud-front": "^1.0.0",
29
+ "@drax/crud-share": "^1.4.0",
30
+ "@drax/media-vue": "^1.0.0"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "pinia": "^2.2.2",
@@ -64,5 +64,5 @@
64
64
  "vue-tsc": "^2.1.10",
65
65
  "vuetify": "^3.8.2"
66
66
  },
67
- "gitHead": "a8fe4fe11637e3b899686b8dd8d658d62933ea5a"
67
+ "gitHead": "b957d2b908e730535cf9fb628f0c57e20c007d0f"
68
68
  }
package/src/EntityCrud.ts CHANGED
@@ -111,6 +111,8 @@ class EntityCrud implements IEntityCrud {
111
111
  value = field.default
112
112
  } else if (field.type === 'object') {
113
113
  value = this.objectFields(field)
114
+ } else if (field.type === 'record') {
115
+ value = {}
114
116
  } else if (/array/.test(field.type)) {
115
117
  value = []
116
118
  } else {
@@ -3,6 +3,7 @@ import {computed, ref} from "vue";
3
3
  import type {PropType} from "vue";
4
4
  import CrudFormList from "./CrudFormList.vue";
5
5
  import CrudAutocomplete from "./CrudAutocomplete.vue";
6
+ import CrudFormRecord from "./CrudFormRecord.vue";
6
7
  import {useI18n} from "vue-i18n";
7
8
  import {useCrudStore} from "../stores/UseCrudStore";
8
9
  import {VDateInput} from 'vuetify/labs/VDateInput'
@@ -538,6 +539,21 @@ defineEmits(['updateValue'])
538
539
  @updateValue="$emit('updateValue')"
539
540
  />
540
541
 
542
+ <crud-form-record
543
+ v-if="field.type === 'record'"
544
+ :entity="entity"
545
+ :field="field"
546
+ v-model="valueModel"
547
+ :readonly="readonly"
548
+ :density="density"
549
+ :variant="variant"
550
+ :clearable="clearable"
551
+ :hide-details="hideDetails"
552
+ :single-line="singleLine"
553
+ :error-messages="inputErrors"
554
+ @updateValue="$emit('updateValue')"
555
+ />
556
+
541
557
  </div>
542
558
  </template>
543
559
 
@@ -0,0 +1,166 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch } from 'vue'
3
+ import type { PropType } from 'vue'
4
+ import type { IEntityCrud, IEntityCrudField } from '@drax/crud-share'
5
+ import { useI18n } from 'vue-i18n'
6
+
7
+ const { t } = useI18n()
8
+
9
+ const valueModel = defineModel<Record<string, any>>({ type: Object, default: () => ({}) })
10
+
11
+ const { field, readonly, density, variant } = defineProps({
12
+ entity: { type: Object as PropType<IEntityCrud>, required: true },
13
+ field: { type: Object as PropType<IEntityCrudField>, required: true },
14
+ readonly: { type: Boolean, default: false },
15
+ density: { type: String as PropType<'comfortable' | 'compact' | 'default'>, default: 'default' },
16
+ variant: {
17
+ type: String as PropType<'underlined' | 'outlined' | 'filled' | 'solo' | 'solo-inverted' | 'solo-filled' | 'plain'>,
18
+ default: 'filled'
19
+ },
20
+ clearable: { type: Boolean, default: false },
21
+ hideDetails: { type: Boolean, default: false },
22
+ singleLine: { type: Boolean, default: false },
23
+ errorMessages: { type: Array as PropType<string[]>, default: () => [] }
24
+ })
25
+
26
+ // Mantener un array local de entradas con IDs únicos
27
+ const localEntries = ref<Array<{ id: string; key: string; value: string }>>([])
28
+ let nextId = 0
29
+ let isUpdatingFromModel = false
30
+
31
+ // Inicializar desde valueModel
32
+ const initializeEntries = () => {
33
+ const modelEntries = Object.entries(valueModel.value || {})
34
+ if (modelEntries.length > 0) {
35
+ localEntries.value = modelEntries.map(([k, v]) => ({
36
+ id: `entry-${nextId++}`,
37
+ key: k,
38
+ value: v as string
39
+ }))
40
+ }
41
+ }
42
+
43
+ // Inicializar al montar
44
+ initializeEntries()
45
+
46
+ // Sincronizar cuando cambia valueModel externamente (no desde updateModel)
47
+ watch(() => valueModel.value, () => {
48
+ if (!isUpdatingFromModel) {
49
+ initializeEntries()
50
+ }
51
+ isUpdatingFromModel = false
52
+ }, { deep: true })
53
+
54
+ const addEntry = () => {
55
+ localEntries.value.push({
56
+ id: `entry-${nextId++}`,
57
+ key: '',
58
+ value: ''
59
+ })
60
+ }
61
+
62
+ const removeEntry = (idToRemove: string) => {
63
+ localEntries.value = localEntries.value.filter(entry => entry.id !== idToRemove)
64
+ updateModel()
65
+ }
66
+
67
+ const updateKey = (id: string, newKey: string) => {
68
+ const entry = localEntries.value.find(e => e.id === id)
69
+ if (entry) {
70
+ entry.key = newKey
71
+ updateModel()
72
+ }
73
+ }
74
+
75
+ const updateValue = (id: string, newValue: string) => {
76
+ const entry = localEntries.value.find(e => e.id === id)
77
+ if (entry) {
78
+ entry.value = newValue
79
+ updateModel()
80
+ }
81
+ }
82
+
83
+ const updateModel = () => {
84
+ isUpdatingFromModel = true
85
+ const filtered = localEntries.value.filter(entry => entry.key.trim() !== '' && entry.value.trim() !== '')
86
+ valueModel.value = Object.fromEntries(filtered.map(entry => [entry.key, entry.value]))
87
+ }
88
+
89
+ defineEmits(['updateValue'])
90
+ </script>
91
+
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>
99
+ </div>
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">
104
+ <v-text-field
105
+ :model-value="entry.key"
106
+ :label="t('common.key') || 'Key'"
107
+ :density="density"
108
+ :variant="variant"
109
+ :readonly="readonly"
110
+ @update:model-value="(v) => updateKey(entry.id, v)"
111
+ @blur="$emit('updateValue')"
112
+ outlined
113
+ />
114
+ </v-col>
115
+ <v-col cols="12" sm="5">
116
+ <v-text-field
117
+ :model-value="entry.value"
118
+ :label="t('common.value') || 'Value'"
119
+ :density="density"
120
+ :variant="variant"
121
+ :readonly="readonly"
122
+ @update:model-value="(v) => updateValue(entry.id, v)"
123
+ @blur="$emit('updateValue')"
124
+ outlined
125
+ />
126
+ </v-col>
127
+ <v-col cols="12" sm="2" class="d-flex align-center">
128
+ <v-btn
129
+ v-if="!readonly"
130
+ icon="mdi-delete"
131
+ size="small"
132
+ color="error"
133
+ variant="text"
134
+ @click="removeEntry(entry.id)"
135
+ />
136
+ </v-col>
137
+ </v-row>
138
+ </div>
139
+
140
+ <v-divider v-if="localEntries.length > 0" class="my-3" />
141
+
142
+ <v-btn
143
+ v-if="!readonly"
144
+ prepend-icon="mdi-plus"
145
+ color="primary"
146
+ variant="tonal"
147
+ block
148
+ @click="addEntry"
149
+ >
150
+ {{ t('action.addEntry') || 'Add Entry' }}
151
+ </v-btn>
152
+ </v-card-text>
153
+ </v-card>
154
+ </div>
155
+ </template>
156
+
157
+ <style scoped>
158
+ .record-field {
159
+ width: 100%;
160
+ }
161
+
162
+ .record-entry {
163
+ border-left: 3px solid rgba(0, 0, 0, 0.12);
164
+ padding-left: 12px;
165
+ }
166
+ </style>