@bildvitta/quasar-ui-asteroid 3.19.0-beta.3 → 3.19.0-beta.4

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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bildvitta/quasar-ui-asteroid",
3
3
  "description": "Asteroid",
4
- "version": "3.19.0-beta.3",
4
+ "version": "3.19.0-beta.4",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "./src/asteroid.js",
package/src/asteroid.js CHANGED
@@ -77,8 +77,10 @@ export const QasTableGenerator = defineAsyncComponent(() => import('./components
77
77
  export const QasTabsGenerator = defineAsyncComponent(() => import('./components/tabs-generator/QasTabsGenerator.vue'))
78
78
  export const QasTextTruncate = defineAsyncComponent(() => import('./components/text-truncate/QasTextTruncate.vue'))
79
79
  export const QasTimeline = defineAsyncComponent(() => import('./components/timeline/QasTimeline.vue'))
80
+ export const QasTip = defineAsyncComponent(() => import('./components/tip/QasTip.vue'))
80
81
  export const QasToggle = defineAsyncComponent(() => import('./components/toggle/QasToggle.vue'))
81
82
  export const QasToggleVisibility = defineAsyncComponent(() => import('./components/toggle-visibility/QasToggleVisibility.vue'))
83
+ export const QasTooltip = defineAsyncComponent(() => import('./components/tooltip/QasTooltip.vue'))
82
84
  export const QasTransfer = defineAsyncComponent(() => import('./components/transfer/QasTransfer.vue'))
83
85
  export const QasTreeGenerator = defineAsyncComponent(() => import('./components/tree-generator/QasTreeGenerator.vue'))
84
86
  export const QasUploader = defineAsyncComponent(() => import('./components/uploader/QasUploader.vue'))
@@ -24,7 +24,7 @@
24
24
  </div>
25
25
  </div>
26
26
 
27
- <div v-if="fieldsetItem.__hasSubset" class="column q-col-gutter-y-xl q-mt-none">
27
+ <div v-if="fieldsetItem.__hasSubset" class="column q-col-gutter-y-md q-mt-none">
28
28
  <div v-for="(subsetItem, subsetKey) in fieldsetItem.subset" :key="subsetKey" class="col-12">
29
29
  <slot :name="`legend-${fieldsetItemKey}-${subsetKey}`">
30
30
  <qas-header v-if="subsetItem.__hasHeader" v-bind="getHeaderProps({ values: subsetItem, isSubset: true} )" />
@@ -127,7 +127,13 @@ const emit = defineEmits(['update:modelValue'])
127
127
  provide('isFormGenerator', true)
128
128
 
129
129
  // composables
130
- const { classes, getFieldClass, getFieldSetColumnClass } = useGenerator({ props })
130
+ const {
131
+ classes,
132
+ getFieldClass,
133
+ getFieldSetColumnClass,
134
+ getHeaderProps,
135
+ getNormalizedFields
136
+ } = useGenerator({ props })
131
137
 
132
138
  const { fieldsetClasses, hasFieldset } = useFieldset({ props })
133
139
 
@@ -189,70 +195,13 @@ const normalizedFields = computed(() => {
189
195
  }
190
196
  }
191
197
 
192
- return getNormalizedFields(props.fieldset)
198
+ return getNormalizedFields({
199
+ items: props.fieldset,
200
+ fields: props.fields,
201
+ result: props.modelValue
202
+ })
193
203
  })
194
204
 
195
- /**
196
- * Função recursiva na qual se é utilizada para definir a estrutura de um fieldset e também de um subset (presente dentro do fieldset).
197
- *
198
- * @param items {Object} items - Objeto contendo a estrutura de um fieldset ou subset
199
- */
200
- function getNormalizedFields (items) {
201
- const result = {}
202
-
203
- for (const key in items) {
204
- const item = items[key]
205
-
206
- // Propriedades que são recebidas em cada fieldset / subset desconstruida
207
- const {
208
- label,
209
- description,
210
- headerProps,
211
- column,
212
- buttonProps,
213
- fields = [],
214
- subset = {}
215
- } = item
216
-
217
- // Valida caso tenha um header
218
- const hasHeader = !!(label || description || Object.keys(headerProps || {}).length)
219
-
220
- // Valida caso tenha um subset dentro de um fieldset
221
- const hasSubset = !!Object.keys(subset).length
222
-
223
- // Estrutura de um fieldset e subset
224
- const structure = {
225
- label,
226
- description,
227
- column,
228
- buttonProps,
229
- headerProps,
230
- fields: {},
231
- subset: hasSubset ? getNormalizedFields(subset) : {},
232
-
233
- // Propriedades auxiliares para controle na view
234
- __hasHeader: hasHeader,
235
- __hasSubset: hasSubset,
236
- __hasFieldset: true
237
- }
238
-
239
- // Percorre as lista de fields (keys) que estão dentro do fieldset / subset
240
- fields.forEach(fieldKey => {
241
- // Encontra dentro da prop fields o field correspondente a key
242
- const field = props.fields[fieldKey]
243
-
244
- if (!field) return
245
-
246
- // Adiciona o field ao fields da estrutura
247
- Object.assign(structure.fields, { [fieldKey]: field })
248
- })
249
-
250
- result[key] = structure
251
- }
252
-
253
- return result
254
- }
255
-
256
205
  const fieldContainerClasses = computed(() => {
257
206
  return [
258
207
  ...classes.value,
@@ -267,34 +216,6 @@ function getFieldType ({ type }) {
267
216
  return type === 'hidden' ? 'hidden' : 'visible'
268
217
  }
269
218
 
270
- /**
271
- * Retorna as propriedades de um header.
272
- *
273
- * @param {Object} values - Contém as propriedades de um fieldset/subset.
274
- * @param {Boolean} isSubset - Verifica se o header será usado em um subset.
275
- *
276
- * @returns {Object} Retorna as propriedades do header.
277
- */
278
- function getHeaderProps ({ values, isSubset }) {
279
- const typography = isSubset ? 'h5' : 'h4'
280
-
281
- // Separa as propriedades de label e o restante.
282
- const { labelProps, ...headerProps } = values.headerProps || {}
283
-
284
- return {
285
- description: values.description,
286
-
287
- // Mesmo que passado a tipografia no headerProps, a tipografia é sobreposta para manter a hierarquia sempre.
288
- labelProps: {
289
- ...labelProps,
290
- ...(values.label && { label: values.label }),
291
- typography
292
- },
293
-
294
- ...headerProps
295
- }
296
- }
297
-
298
219
  function isFieldDisabled ({ disable }) {
299
220
  return disable || props.disable
300
221
  }
@@ -1,32 +1,76 @@
1
1
  <template>
2
2
  <component :is="component.is" v-bind="component.props">
3
- <qas-header v-if="hasHeader" v-bind="props.headerProps" />
4
-
5
- <div :class="classes">
6
- <div v-for="(field, key) in fieldsByResult" :key="key" :class="getContainerClasses({ key })">
7
- <slot :field="field" :name="`field-${field.name}`">
8
- <qas-grid-item :use-ellipsis="hasEllipsis(field)" :use-inline="props.useInline">
9
- <template #header>
10
- <slot :field="field" :name="`header-field-${field.name}`">
11
- <slot :field="field" name="header">
12
- <div :class="headerClass" :data-cy="`grid-generator-${field.name}-field`" :title="getTitle(field, 'label')">
13
- {{ field.label }}
14
- </div>
3
+ <div class="column q-col-gutter-y-lg">
4
+ <div v-for="(fieldsetItem, fieldsetItemKey) in normalizedFields" :key="fieldsetItemKey">
5
+ <q-separator v-if="hasSeparator({ item: fieldsetItem })" class="q-mb-lg" />
6
+
7
+ <qas-header v-if="fieldsetItem.__hasHeader" v-bind="getHeaderProps({ values: fieldsetItem })" />
8
+
9
+ <div :class="classes">
10
+ <div v-for="(field, key) in fieldsetItem.fields" :key="key" :class="getContainerClasses({ key })">
11
+ <slot :field="field" :name="`field-${field.name}`">
12
+ <qas-grid-item v-bind="getGridItemProps(field)">
13
+ <template #header>
14
+ <slot :field="field" :name="`header-field-${field.name}`">
15
+ <slot :field="field" name="header">
16
+ <div :class="headerClasses" :data-cy="`grid-generator-${field.name}-field`" :title="getTitle(field, 'label')">
17
+ {{ field.label }}
18
+ </div>
19
+ </slot>
20
+ </slot>
21
+ </template>
22
+
23
+ <template #content>
24
+ <slot :field="field" :name="`content-field-${field.name}`">
25
+ <slot :field="field" name="content">
26
+ <div :class="getContentClasses(field)" :data-cy="`grid-generator-${field.name}-result`" :title="getTitle(field, 'formattedResult')">
27
+ {{ field.formattedResult }}
28
+ </div>
29
+ </slot>
30
+ </slot>
31
+ </template>
32
+ </qas-grid-item>
33
+ </slot>
34
+ </div>
35
+ </div>
36
+
37
+ <div v-if="fieldsetItem.__hasSubset" class="q-gutter-y-md q-mt-md">
38
+ <div v-for="(subsetItem, subsetKey) in fieldsetItem.subset" :key="subsetKey">
39
+ <div v-if="hasSeparator({ item: subsetItem, isSubset: true })" class="q-pb-md">
40
+ <q-separator />
41
+ </div>
42
+
43
+ <qas-header v-if="subsetItem.__hasHeader" v-bind="getHeaderProps({ values: subsetItem, isSubset: true })" />
44
+
45
+ <div :class="classes">
46
+ <div v-for="(field, key) in subsetItem.fields" :key="key" :class="getContainerClasses({ key })">
47
+ <slot :field="field" :name="`field-${field.name}`">
48
+ <qas-grid-item v-bind="getGridItemProps(field)">
49
+ <template #header>
50
+ <slot :field="field" :name="`header-field-${field.name}`">
51
+ <slot :field="field" name="header">
52
+ <div :class="headerClasses" :data-cy="`grid-generator-${field.name}-field`" :title="getTitle(field, 'label')">
53
+ {{ field.label }}
54
+ </div>
55
+ </slot>
56
+ </slot>
57
+ </template>
58
+
59
+ <template #content>
60
+ <slot :field="field" :name="`content-field-${field.name}`">
61
+ <slot :field="field" name="content">
62
+ <div :class="getContentClasses(field)" :data-cy="`grid-generator-${field.name}-result`" :title="getTitle(field, 'formattedResult')">
63
+ {{ field.formattedResult }}
64
+ </div>
65
+ </slot>
66
+ </slot>
67
+ </template>
68
+ </qas-grid-item>
15
69
  </slot>
16
- </slot>
17
- </template>
18
-
19
- <template #content>
20
- <slot :field="field" :name="`content-field-${field.name}`">
21
- <slot :field="field" name="content">
22
- <div :class="getContentClasses(field)" :data-cy="`grid-generator-${field.name}-result`" :title="getTitle(field, 'formattedResult')">
23
- {{ field.formattedResult }}
24
- </div>
25
- </slot>
26
- </slot>
27
- </template>
28
- </qas-grid-item>
29
- </slot>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </div>
30
74
  </div>
31
75
  </div>
32
76
  </component>
@@ -38,10 +82,8 @@ import QasGridItem from '../grid-item/QasGridItem.vue'
38
82
  import QasHeader from '../header/QasHeader.vue'
39
83
 
40
84
  import useGenerator, { baseProps } from '../../composables/private/use-generator'
41
- import { isEmpty, humanize } from '../../helpers'
42
85
  import { useScreen } from '../../composables'
43
- import isObject from 'lodash-es/isObject'
44
- import { ref, computed, watch } from 'vue'
86
+ import { computed } from 'vue'
45
87
 
46
88
  // define component name
47
89
  defineOptions({ name: 'QasGridGenerator' })
@@ -58,13 +100,23 @@ const props = defineProps({
58
100
  },
59
101
 
60
102
  contentClass: {
61
- default: '',
62
- type: [Array, Object, String]
103
+ type: [Array, Object, String],
104
+ default: ''
105
+ },
106
+
107
+ fieldset: {
108
+ type: Object,
109
+ default: () => ({})
110
+ },
111
+
112
+ fieldsProps: {
113
+ type: Object,
114
+ default: () => ({})
63
115
  },
64
116
 
65
117
  headerClass: {
66
- default: '',
67
- type: [Array, Object, String]
118
+ type: [Array, Object, String],
119
+ default: ''
68
120
  },
69
121
 
70
122
  headerProps: {
@@ -73,13 +125,13 @@ const props = defineProps({
73
125
  },
74
126
 
75
127
  emptyResultText: {
76
- default: '-',
77
- type: String
128
+ type: String,
129
+ default: '-'
78
130
  },
79
131
 
80
132
  result: {
81
- default: () => ({}),
82
- type: Object
133
+ type: Object,
134
+ default: () => ({})
83
135
  },
84
136
 
85
137
  useBox: {
@@ -87,13 +139,13 @@ const props = defineProps({
87
139
  },
88
140
 
89
141
  useEmptyResult: {
90
- default: true,
91
- type: Boolean
142
+ type: Boolean,
143
+ default: true
92
144
  },
93
145
 
94
146
  useEllipsis: {
95
- default: true,
96
- type: Boolean
147
+ type: Boolean,
148
+ default: true
97
149
  },
98
150
 
99
151
  useInline: {
@@ -102,13 +154,15 @@ const props = defineProps({
102
154
  })
103
155
 
104
156
  // composables
105
- const { classes, getFieldClass } = useGenerator({ props, isGrid: true })
106
-
107
- // computed
108
- const hasResult = computed(() => Object.keys(props.result).length)
109
- const hasFields = computed(() => Object.keys(props.fields).length)
110
- const hasHeader = computed(() => Object.keys(props.headerProps).length)
111
-
157
+ const {
158
+ classes,
159
+ getFieldClass,
160
+ getHeaderProps,
161
+ getNormalizedFields,
162
+ getFieldsByResult
163
+ } = useGenerator({ props, isGrid: true })
164
+
165
+ // computeds
112
166
  const component = computed(() => {
113
167
  return {
114
168
  is: props.useBox ? QasBox : 'div',
@@ -116,7 +170,7 @@ const component = computed(() => {
116
170
  }
117
171
  })
118
172
 
119
- const headerClass = computed(() => {
173
+ const headerClasses = computed(() => {
120
174
  return [
121
175
  props.headerClass,
122
176
 
@@ -126,65 +180,30 @@ const headerClass = computed(() => {
126
180
  ]
127
181
  })
128
182
 
129
- const fieldsByResult = ref({})
130
-
131
- /**
132
- * Retorna os "fields" formatados, caso a propriedade "useEmptyResult" seja "true", retorna todos os "fields", mesmo que não tenha resultado.
133
- * Caso a propriedade "useEmptyResult" seja "false", retorna apenas os "fields" que possuem resultado.
134
- */
135
- const formattedFields = computed(() => {
136
- if (props.useEmptyResult) return props.fields
137
-
138
- if (!hasResult.value) return {}
139
-
140
- const fields = {}
141
-
142
- for (const key in props.fields) {
143
- const result = props.result[key]
144
-
145
- const validate = Array.isArray(result)
146
- ? result.length
147
- : isObject(result) ? Object.keys(result).length : result
148
-
149
- if (validate) {
150
- fields[key] = props.fields[key]
183
+ const normalizedFields = computed(() => {
184
+ const hasFieldset = !!Object.keys(props.fieldset).length
185
+
186
+ // No caso de não ter "fieldset", virá via props o result e fields.
187
+ if (!hasFieldset) {
188
+ return {
189
+ default: {
190
+ fields: getFieldsByResult({ fields: props.fields, result: props.result }),
191
+ headerProps: props.headerProps,
192
+ __hasHeader: Object.keys(props.headerProps).length
193
+ }
151
194
  }
152
195
  }
153
196
 
154
- return fields
197
+ // No caso de ter "fieldset", deve normalizar os fields/result de acordo com o fieldset.
198
+ return getNormalizedFields({
199
+ items: props.fieldset,
200
+ fields: props.fields,
201
+ result: props.result,
202
+ isGrid: true
203
+ })
155
204
  })
156
205
 
157
- // watch
158
- watch(() => formattedFields.value, setFieldsByResult, { immediate: true })
159
-
160
206
  // functions
161
- function getFieldsByResult () {
162
- if (!hasResult.value || !hasFields.value) return {}
163
-
164
- const result = { ...props.result }
165
- const fieldsByResult = {}
166
-
167
- for (const key in formattedFields.value) {
168
- const field = formattedFields.value[key] || {}
169
-
170
- if (!field.type) continue
171
-
172
- const humanizedResult = humanize(field, result[key])
173
- const formattedResult = isEmpty({ value: humanizedResult }) ? props.emptyResultText : humanizedResult
174
-
175
- fieldsByResult[key] = {
176
- ...field,
177
- formattedResult
178
- }
179
- }
180
-
181
- return fieldsByResult
182
- }
183
-
184
- function setFieldsByResult () {
185
- fieldsByResult.value = getFieldsByResult()
186
- }
187
-
188
207
  function getContainerClasses ({ key }) {
189
208
  if (props.useInline) return 'row justify-between col-12'
190
209
 
@@ -211,4 +230,21 @@ function getContentClasses (field) {
211
230
  }
212
231
  ]
213
232
  }
233
+
234
+ function getGridItemProps (field) {
235
+ return {
236
+ useEllipsis: hasEllipsis(field),
237
+ useInline: props.useInline,
238
+
239
+ ...(props.fieldsProps[field.name] || {})
240
+ }
241
+ }
242
+
243
+ function hasSeparator ({ item, isSubset }) {
244
+ /**
245
+ * No caso de for subset, por padrão terá o separator, à não ser que for passado
246
+ * o separator como false. Caso seja um fieldset, verificar se foi passado a chave "useSeparator".
247
+ */
248
+ return isSubset ? (item.useSeparator ?? true) : item.useSeparator
249
+ }
214
250
  </script>
@@ -1,9 +1,13 @@
1
1
  <template>
2
2
  <div :class="classes.container">
3
- <header v-bind="headerProps">
3
+ <header :class="classes.header">
4
4
  <slot name="header">
5
- {{ props.label }}
5
+ <div v-bind="textAttributes">
6
+ {{ props.label }}
7
+ </div>
6
8
  </slot>
9
+
10
+ <qas-tip v-if="props.tip" class="q-ml-xs" :text="props.tip" />
7
11
  </header>
8
12
 
9
13
  <div v-bind="contentProps">
@@ -15,6 +19,7 @@
15
19
  </template>
16
20
 
17
21
  <script setup>
22
+ import QasTip from '../tip/QasTip.vue'
18
23
  import { useScreen } from '../../composables'
19
24
 
20
25
  import { computed } from 'vue'
@@ -39,6 +44,16 @@ const props = defineProps({
39
44
  value: {
40
45
  type: [String, Number, Boolean],
41
46
  default: ''
47
+ },
48
+
49
+ tip: {
50
+ type: String,
51
+ default: ''
52
+ },
53
+
54
+ content: {
55
+ type: Object,
56
+ default: () => ({})
42
57
  }
43
58
  })
44
59
 
@@ -50,40 +65,50 @@ const hasEllipsis = computed(() => props.useEllipsis && !screen.isSmall)
50
65
  const classes = computed(() => {
51
66
  const isInline = props.useInline && !screen.isSmall
52
67
 
68
+ const { typography } = props.content
69
+
53
70
  return {
54
71
  container: {
55
- flex: isInline,
56
- 'q-col-gutter-x-md': isInline,
57
- 'justify-between': isInline,
58
- 'col-12': isInline,
59
- 'no-wrap': isInline
72
+ 'flex justify-between col-12 no-wrap': isInline
60
73
  },
61
74
 
62
75
  header: {
63
76
  'text-caption': !isInline,
64
- 'text-body1': isInline
77
+
78
+ /**
79
+ * Necessário adicionar o padding à direita no header ao invés do gutter, pois ao usar o gutter no eixo x,
80
+ * ele adiciona um espaçamento à direita do content de forma errada, deixando o espaçamento maior que o do header.
81
+ */
82
+ 'text-body1 q-pr-md': isInline,
83
+
84
+ // classes por conta do tip.
85
+ 'row no-wrap': props.tip,
86
+ 'items-center': props.tip || typography,
87
+ flex: typography
65
88
  },
66
89
 
67
90
  content: {
68
91
  'text-grey-10': true,
69
- 'text-body1': !isInline,
70
- 'text-subtitle1': isInline,
92
+
93
+ // é possível passar uma tipografia personalizada.
94
+ [`text-${typography}`]: typography,
95
+
96
+ 'text-body1': !isInline && !typography,
97
+ 'text-subtitle1': isInline && !typography,
98
+
71
99
  ellipsis: hasEllipsis.value
72
100
  }
73
101
  }
74
102
  })
75
103
 
76
- const headerProps = computed(() => {
77
- return {
78
- class: classes.value.header,
79
- ...(hasEllipsis.value && { title: props.label })
80
- }
81
- })
82
-
83
104
  const contentProps = computed(() => {
84
105
  return {
85
106
  class: classes.value.content,
86
107
  ...(hasEllipsis.value && { title: props.value })
87
108
  }
88
109
  })
110
+
111
+ const textAttributes = computed(() => {
112
+ return hasEllipsis.value ? { title: props.label } : undefined
113
+ })
89
114
  </script>
@@ -0,0 +1,48 @@
1
+ <template>
2
+ <q-icon v-bind="iconProps" aria-hidden="false" :aria-label="props.text" class="qas-tip">
3
+ <qas-tooltip :text="props.text" />
4
+ </q-icon>
5
+ </template>
6
+
7
+ <script setup>
8
+ import { computed } from 'vue'
9
+
10
+ defineOptions({ name: 'QasTip' })
11
+
12
+ const props = defineProps({
13
+ text: {
14
+ type: String,
15
+ default: ''
16
+ },
17
+
18
+ icon: {
19
+ type: String,
20
+ default: 'sym_r_help'
21
+ },
22
+
23
+ size: {
24
+ type: String,
25
+ default: '16px'
26
+ },
27
+
28
+ color: {
29
+ type: String,
30
+ default: 'grey-8'
31
+ }
32
+ })
33
+
34
+ // computeds
35
+ const iconProps = computed(() => {
36
+ return {
37
+ name: props.icon,
38
+ size: props.size,
39
+ color: props.color
40
+ }
41
+ })
42
+ </script>
43
+
44
+ <style lang="scss">
45
+ .qas-tip {
46
+ cursor: help;
47
+ }
48
+ </style>
@@ -0,0 +1,26 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Componente de dica com tooltip
5
+
6
+ props:
7
+ text:
8
+ desc: Valor exibido no tooltip
9
+ default: ''
10
+ type: String
11
+
12
+ icon:
13
+ desc: Ícone utilizado no tip.
14
+ default: sym_r_help
15
+ type: String
16
+
17
+ size:
18
+ desc: Tamanho do ícone.
19
+ type: String
20
+ default: 16px
21
+ examples: ['sm', '24px']
22
+
23
+ color:
24
+ desc: Cor do ícone.
25
+ type: String
26
+ default: grey-8
@@ -0,0 +1,23 @@
1
+ <template>
2
+ <q-tooltip v-bind="tooltipProps" class="bg-grey-10">
3
+ {{ props.text }}
4
+ </q-tooltip>
5
+ </template>
6
+
7
+ <script setup>
8
+ defineOptions({ name: 'QasTooltip' })
9
+
10
+ const props = defineProps({
11
+ text: {
12
+ type: String,
13
+ default: ''
14
+ }
15
+ })
16
+
17
+ // consts
18
+ const tooltipProps = {
19
+ anchor: 'center right',
20
+ self: 'center left',
21
+ offset: [5, 5]
22
+ }
23
+ </script>
@@ -0,0 +1,10 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Componente wrapper do [QTooltip](https://quasar.dev/vue-components/tooltip#introduction).
5
+
6
+ props:
7
+ text:
8
+ desc: Texto exibido no tooltip
9
+ default: ''
10
+ type: String
@@ -1,6 +1,8 @@
1
+ import isObject from 'lodash-es/isObject'
1
2
  import { Spacing } from '../../enums/Spacing'
2
3
  import { gutterValidator } from '../../helpers/private/gutter-validator'
3
4
  import useScreen from '../use-screen'
5
+ import { isEmpty, humanize, filterObject } from '../../helpers'
4
6
 
5
7
  import { computed } from 'vue'
6
8
 
@@ -90,6 +92,209 @@ export default function ({ props = {}, isGrid = false }) {
90
92
  return typeof column === 'string' ? _getStringColumns(column) : _getBreakpoint(column)
91
93
  }
92
94
 
95
+ /**
96
+ * Normaliza as propriedades do header, e a tipografia com base se é um subset.
97
+ *
98
+ * @function
99
+ * @param {Object} options
100
+ * @param {Object} options.values - Contém as propriedades de um fieldset/subset.
101
+ * @param {boolean} options.isSubset - Verifica se o header será usado em um subset.
102
+ *
103
+ * @returns {Object} Retorna as propriedades do header.
104
+ */
105
+ function getHeaderProps ({ values, isSubset }) {
106
+ const typography = isSubset ? 'h5' : 'h4'
107
+
108
+ // Separa as propriedades de label e o restante.
109
+ const { labelProps, ...headerProps } = values.headerProps || {}
110
+
111
+ return {
112
+ description: values.description,
113
+
114
+ // Mesmo que passado a tipografia no headerProps, a tipografia é sobreposta para manter a hierarquia sempre.
115
+ labelProps: {
116
+ ...labelProps,
117
+ ...(values.label && { label: values.label }),
118
+ typography
119
+ },
120
+
121
+ ...headerProps
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Normaliza o objeto para retornar um fieldset.
127
+ *
128
+ * @function
129
+ * @param {Object} params
130
+ * @param {Object} params.items - Objeto contendo a estrutura de um fieldset ou subset
131
+ * @param {Object} params.fields - Campos
132
+ * @param {Object} params.result - Resultado com base nos fields
133
+ * @param {boolean} params.isGrid - Chave para indicar se é um grid-generator.
134
+ * @param {boolean} params.useEmptyResult - Se o resultado de algum campo for vazio e esta propriedade for "false", ele remove todo o campo.
135
+ *
136
+ * @returns {Object} - Retorna um objeto com a estrutura do fieldset.
137
+ */
138
+ function getNormalizedFields (params) {
139
+ const result = {}
140
+
141
+ for (const key in params.items) {
142
+ const item = params.items[key]
143
+
144
+ // Propriedades que são recebidas em cada fieldset / subset desconstruida
145
+ const {
146
+ label,
147
+ description,
148
+ headerProps,
149
+ column,
150
+ buttonProps,
151
+ fields = [],
152
+ subset = {},
153
+ useSeparator
154
+ } = item
155
+
156
+ // Valida caso tenha um header
157
+ const hasHeader = !!(label || description || Object.keys(headerProps || {}).length)
158
+
159
+ // Valida caso tenha um subset dentro de um fieldset
160
+ const hasSubset = !!Object.keys(subset).length
161
+
162
+ // Estrutura de um fieldset e subset
163
+ const structure = {
164
+ label,
165
+ description,
166
+ column,
167
+ buttonProps,
168
+ headerProps,
169
+ useSeparator,
170
+ fields: {},
171
+
172
+ subset: hasSubset
173
+ ? getNormalizedFields({
174
+ items: subset,
175
+ isGrid: params.isGrid,
176
+ fields: params.fields,
177
+ result: params.result,
178
+ useEmptyResult: params.useEmptyResult
179
+ })
180
+ : {},
181
+
182
+ // Propriedades auxiliares para controle na view
183
+ __hasHeader: hasHeader,
184
+ __hasSubset: hasSubset,
185
+ __hasFieldset: true
186
+ }
187
+
188
+ // Pegar os fields com base na key.
189
+ const fieldsByfieldsKeys = filterObject(params.fields, fields)
190
+
191
+ /**
192
+ * Foi adicionado essa lógica para o grid-generator, pois cada field deve ter o "formattedResult", onde o
193
+ * "formattedResult" é basicamente o result formatado com base no tipo do field através do humanize,
194
+ * exemplo: field do tipo "date", virá do back "2025-08-20", e deverá exibir "20/08/2025".
195
+ */
196
+ if (params.isGrid) {
197
+ // Adicionar "formattedResult" com base no result.
198
+ const formattedFields = getFieldsByResult({
199
+ fields: fieldsByfieldsKeys,
200
+ result: params.result,
201
+ useEmptyResult: params.useEmptyResult
202
+ })
203
+
204
+ // Adiciona o fields no structure
205
+ Object.assign(structure.fields, formattedFields)
206
+ } else {
207
+ Object.assign(structure.fields, fieldsByfieldsKeys)
208
+ }
209
+
210
+ result[key] = structure
211
+ }
212
+
213
+ return result
214
+ }
215
+
216
+ /**
217
+ * Para todos campos passados, adiciona o "formattedResult" já formatado com base no result
218
+ *
219
+ * @function
220
+ * @param {Object} params
221
+ * @param {Object} params.fields - Campos
222
+ * @param {Object} params.result - Resultado com base nos fields
223
+ * @param {boolean} params.useEmptyResult - Se o resultado de algum campo for vazio e esta propriedade for "false", ele remove todo o campo.
224
+ *
225
+ * @returns {Object} - Retorna os "fields" com um "formattedResult" pra cada field.
226
+ */
227
+ function getFieldsByResult ({ fields, result, useEmptyResult }) {
228
+ const hasFields = Object.keys(fields).length
229
+ const hasResult = Object.keys(result).length
230
+
231
+ if (!hasResult || !hasFields) return {}
232
+
233
+ const unformattedResult = { ...result }
234
+ const fieldsByResult = {}
235
+
236
+ /**
237
+ * Retorna os "fields" com base na prop "useEmptyResult". se ela for "false",
238
+ * ela remove todo campo com valor vazio.
239
+ */
240
+ const formattedFields = getFormattedFields({ fields, result, hasResult, useEmptyResult })
241
+
242
+ for (const key in formattedFields) {
243
+ const field = formattedFields[key] || {}
244
+
245
+ if (!field.type) continue
246
+
247
+ // Formata o result com base no "type", exemplo tipo date: "2025-08-20" -> "20/08/2025"
248
+ const humanizedResult = humanize(field, unformattedResult[key])
249
+
250
+ const formattedResult = isEmpty({ value: humanizedResult }) ? props.emptyResultText : humanizedResult
251
+
252
+ fieldsByResult[key] = {
253
+ ...field,
254
+ formattedResult
255
+ }
256
+ }
257
+
258
+ return fieldsByResult
259
+ }
260
+
261
+ /**
262
+ * @private
263
+ *
264
+ * @function
265
+ * @param {Object} params
266
+ * @param {Object} params.fields - Campos
267
+ * @param {Object} params.result - Resultado com base nos fields
268
+ * @param {boolean} params.hasResult - Casotenha resultado
269
+ * @param {boolean} params.useEmptyResult - Se o resultado de algum campo for vazio e esta propriedade for "false", ele remove todo o campo.
270
+ *
271
+ * @returns {Object} - Retorna os "fields" formatados, caso a propriedade "useEmptyResult" seja "true",
272
+ * retorna todos os "fields", mesmo que não tenha resultado. Caso a propriedade "useEmptyResult" seja "false",
273
+ * retorna apenas os "fields" que possuem resultado.
274
+ */
275
+ function getFormattedFields ({ fields, result, hasResult, useEmptyResult }) {
276
+ if (useEmptyResult) return fields
277
+
278
+ if (!hasResult) return {}
279
+
280
+ const formattedFields = {}
281
+
282
+ for (const key in fields) {
283
+ const currentResult = result[key]
284
+
285
+ // Verifica se o field tem result, caso tenha, adiciona no "formattedFields"
286
+ const validate = Array.isArray(currentResult)
287
+ ? currentResult.length
288
+ : isObject(currentResult) ? Object.keys(currentResult).length : result
289
+
290
+ if (validate) {
291
+ formattedFields[key] = fields[key]
292
+ }
293
+ }
294
+
295
+ return formattedFields
296
+ }
297
+
93
298
  /**
94
299
  * @private
95
300
  */
@@ -184,6 +389,10 @@ export default function ({ props = {}, isGrid = false }) {
184
389
  classes,
185
390
 
186
391
  getFieldClass,
187
- getFieldSetColumnClass
392
+ getFieldSetColumnClass,
393
+
394
+ getHeaderProps,
395
+ getNormalizedFields,
396
+ getFieldsByResult
188
397
  }
189
398
  }