@bildvitta/quasar-ui-asteroid 3.17.0-beta.3 → 3.17.0-beta.5
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 +1 -1
- package/src/components/actions/QasActions.vue +1 -1
- package/src/components/chart-view/QasChartView.vue +40 -18
- package/src/components/chart-view/QasChartView.yml +10 -0
- package/src/components/dialog/QasDialog.vue +3 -3
- package/src/components/filters/QasFilters.vue +1 -1
- package/src/components/filters/private/PvFiltersButton.vue +1 -1
- package/src/components/form-generator/QasFormGenerator.vue +7 -10
- package/src/components/grid-generator/QasGridGenerator.vue +21 -14
- package/src/components/header/QasHeader.vue +15 -5
- package/src/components/nested-fields/QasNestedFields.vue +91 -36
- package/src/components/nested-fields/QasNestedFields.yml +23 -0
- package/src/components/select/QasSelect.vue +86 -0
- package/src/components/select/QasSelect.yml +6 -0
- package/src/components/text-truncate/QasTextTruncate.vue +8 -1
- package/src/components/text-truncate/QasTextTruncate.yml +4 -0
- package/src/composables/private/use-generator.js +3 -9
- package/src/css/components/item.scss +5 -1
- package/src/enums/Spacing.js +33 -0
package/package.json
CHANGED
|
@@ -72,7 +72,7 @@ const classes = computed(() => {
|
|
|
72
72
|
const columnClasses = computed(() => {
|
|
73
73
|
if (props.useEqualWidth) return 'col-12 col-sm-6'
|
|
74
74
|
|
|
75
|
-
return props.useFullWidth ? '
|
|
75
|
+
return props.useFullWidth ? 'full-width' : 'col-12 col-sm-auto'
|
|
76
76
|
})
|
|
77
77
|
|
|
78
78
|
const hasPrimarySlot = computed(() => !!slots.primary)
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
|
|
2
|
+
<component :is="parentComponent.is" v-bind="parentComponent.props">
|
|
3
|
+
<qas-header v-if="hasHeader" v-bind="headerProps">
|
|
4
|
+
<template #right>
|
|
5
|
+
<qas-filters v-bind="chartFiltersProps" />
|
|
6
|
+
</template>
|
|
7
|
+
</qas-header>
|
|
8
|
+
|
|
9
|
+
<div v-bind="parentComponentProps">
|
|
10
|
+
<component :is="chartComponent.is" v-if="showChart" v-bind="chartComponent.props" />
|
|
11
|
+
|
|
12
|
+
<div v-else-if="!isFetching">
|
|
13
|
+
<slot name="empty-results">
|
|
14
|
+
<qas-empty-result-text />
|
|
15
|
+
</slot>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<q-inner-loading :showing="isFetching">
|
|
19
|
+
<q-spinner color="grey" size="3em" />
|
|
20
|
+
</q-inner-loading>
|
|
15
21
|
</div>
|
|
16
|
-
|
|
17
|
-
<q-inner-loading :showing="isFetching">
|
|
18
|
-
<q-spinner color="grey" size="3em" />
|
|
19
|
-
</q-inner-loading>
|
|
20
|
-
</div>
|
|
22
|
+
</component>
|
|
21
23
|
</template>
|
|
22
24
|
|
|
23
25
|
<script>
|
|
@@ -69,6 +71,11 @@ export default {
|
|
|
69
71
|
type: Function
|
|
70
72
|
},
|
|
71
73
|
|
|
74
|
+
boxProps: {
|
|
75
|
+
default: () => ({}),
|
|
76
|
+
type: Object
|
|
77
|
+
},
|
|
78
|
+
|
|
72
79
|
colorsList: {
|
|
73
80
|
type: Array,
|
|
74
81
|
default: () => []
|
|
@@ -124,6 +131,11 @@ export default {
|
|
|
124
131
|
type: String
|
|
125
132
|
},
|
|
126
133
|
|
|
134
|
+
useBox: {
|
|
135
|
+
type: Boolean,
|
|
136
|
+
default: true
|
|
137
|
+
},
|
|
138
|
+
|
|
127
139
|
useFilterButton: {
|
|
128
140
|
type: Boolean
|
|
129
141
|
}
|
|
@@ -308,6 +320,16 @@ export default {
|
|
|
308
320
|
label: this.title
|
|
309
321
|
}
|
|
310
322
|
}
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
parentComponent () {
|
|
326
|
+
return {
|
|
327
|
+
is: this.useBox ? 'qas-box' : 'div',
|
|
328
|
+
|
|
329
|
+
props: {
|
|
330
|
+
...(this.useBox && { ...this.boxProps })
|
|
331
|
+
}
|
|
332
|
+
}
|
|
311
333
|
}
|
|
312
334
|
},
|
|
313
335
|
|
|
@@ -10,6 +10,11 @@ props:
|
|
|
10
10
|
type: Function
|
|
11
11
|
examples: ['beforeFetch({ payload, resolve, done })']
|
|
12
12
|
|
|
13
|
+
box-props:
|
|
14
|
+
desc: Propriedades do "QasBox" que envolve o conteúdo.
|
|
15
|
+
default: {}
|
|
16
|
+
type: Object
|
|
17
|
+
|
|
13
18
|
colors-list:
|
|
14
19
|
desc: Lista de cores personalizadas para utilizar nos gráficos
|
|
15
20
|
default: []
|
|
@@ -65,6 +70,11 @@ props:
|
|
|
65
70
|
desc: Envia como parâmetro para a action "fetchList" do modulo correspondente a "entity".
|
|
66
71
|
type: String
|
|
67
72
|
|
|
73
|
+
use-box:
|
|
74
|
+
desc: Controla se o componente vai ter o QasBox englobando ou não.
|
|
75
|
+
default: true
|
|
76
|
+
type: Boolean
|
|
77
|
+
|
|
68
78
|
use-filter-button:
|
|
69
79
|
desc: Controla se o componente vai usar ou não o componente "QasFilters".
|
|
70
80
|
type: Boolean
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<q-dialog ref="dialogRef" class="qas-dialog" :class="classes" data-cy="dialog" v-bind="dialogProps" :persistent="props.persistent" @update:model-value="updateModelValue">
|
|
3
|
-
<div class="bg-white q-pa-
|
|
4
|
-
<header v-if="hasHeader" class="q-mb-
|
|
3
|
+
<div class="bg-white q-pa-md" :style="style">
|
|
4
|
+
<header v-if="hasHeader" class="q-mb-md">
|
|
5
5
|
<slot name="header">
|
|
6
6
|
<div class="items-center justify-between row">
|
|
7
|
-
<
|
|
7
|
+
<qas-label data-cy="dialog-title" :label="props.card.title" margin="none" />
|
|
8
8
|
|
|
9
9
|
<qas-btn v-if="isInfoDialog" v-close-popup color="grey-10" data-cy="dialog-close-btn" icon="sym_r_close" variant="tertiary" />
|
|
10
10
|
</div>
|
|
@@ -160,7 +160,7 @@ export default {
|
|
|
160
160
|
const field = { ...this.fields[key], ...this.formattedFieldsProps?.[decamelize(key)] }
|
|
161
161
|
const value = humanize(field, this.normalizeValues(filters[key], field?.multiple))
|
|
162
162
|
|
|
163
|
-
if (!value) continue
|
|
163
|
+
if (!value || (Array.isArray(value) && !value.length)) continue
|
|
164
164
|
|
|
165
165
|
const { label, name } = field
|
|
166
166
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<qas-btn data-cy="filters-btn" icon="sym_r_filter_alt" label="Filtrar" :use-label-on-small-screen="false" variant="tertiary">
|
|
3
|
-
<q-menu ref="menu" anchor="center right" class="full-width" max-width="270px"
|
|
3
|
+
<q-menu ref="menu" anchor="center right" class="full-width" max-width="270px" v-bind="menuProps" no-refocus self="top right">
|
|
4
4
|
<div v-if="loading" class="q-pa-xl text-center">
|
|
5
5
|
<q-spinner color="grey" size="2em" />
|
|
6
6
|
</div>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div :class="fieldsetClasses">
|
|
3
3
|
<div v-for="(fieldsetItem, fieldsetItemKey) in normalizedFields" :key="fieldsetItemKey" :class="getFieldSetColumnClass(fieldsetItem.column)">
|
|
4
4
|
<component :is="containerComponent.is" v-bind="containerComponent.props">
|
|
5
|
-
<slot v-if="
|
|
5
|
+
<slot v-if="fieldset.__isFieldset" :name="`legend-${fieldsetItemKey}`">
|
|
6
6
|
<qas-header v-bind="getHeaderProps(fieldsetItem)" />
|
|
7
7
|
</slot>
|
|
8
8
|
|
|
@@ -97,7 +97,7 @@ provide('isFormGenerator', true)
|
|
|
97
97
|
// composables
|
|
98
98
|
const { classes, getFieldClass, getFieldSetColumnClass } = useGenerator({ props })
|
|
99
99
|
|
|
100
|
-
const { fieldsetClasses, hasFieldset
|
|
100
|
+
const { fieldsetClasses, hasFieldset } = useFieldset({ props })
|
|
101
101
|
|
|
102
102
|
const screen = useScreen()
|
|
103
103
|
|
|
@@ -169,7 +169,10 @@ const normalizedFields = computed(() => {
|
|
|
169
169
|
column: fieldsetItem.column,
|
|
170
170
|
buttonProps: fieldsetItem.buttonProps,
|
|
171
171
|
fields: { hidden: {}, visible: {} },
|
|
172
|
-
headerProps: fieldsetItem.headerProps
|
|
172
|
+
headerProps: fieldsetItem.headerProps,
|
|
173
|
+
|
|
174
|
+
// Indica que existe um fieldset para que o QasHeader possa ser renderizado
|
|
175
|
+
__isFieldset: true
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
fieldsetItem.fields.forEach(fieldName => {
|
|
@@ -248,15 +251,9 @@ function useFieldset ({ props }) {
|
|
|
248
251
|
|
|
249
252
|
const hasFieldset = computed(() => !!Object.keys(props.fieldset).length)
|
|
250
253
|
|
|
251
|
-
function hasFieldsetItem (fieldset = {}) {
|
|
252
|
-
return !!Object.keys(fieldset).length
|
|
253
|
-
}
|
|
254
|
-
|
|
255
254
|
return {
|
|
256
255
|
fieldsetClasses,
|
|
257
|
-
hasFieldset
|
|
258
|
-
|
|
259
|
-
hasFieldsetItem
|
|
256
|
+
hasFieldset
|
|
260
257
|
}
|
|
261
258
|
}
|
|
262
259
|
</script>
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
<qas-header v-if="hasHeader" v-bind="props.headerProps" />
|
|
4
4
|
|
|
5
5
|
<div :class="classes">
|
|
6
|
-
<div v-for="(field, key) in fieldsByResult" :key="key" :class="
|
|
6
|
+
<div v-for="(field, key) in fieldsByResult" :key="key" :class="getContainerClassses({ key })">
|
|
7
7
|
<slot :field="field" :name="`field-${field.name}`">
|
|
8
|
-
<qas-grid-item :use-ellipsis="
|
|
8
|
+
<qas-grid-item :use-ellipsis="hasEllipsis(field)" :use-inline="props.useInline">
|
|
9
9
|
<template #header>
|
|
10
10
|
<slot :field="field" :name="`header-field-${field.name}`">
|
|
11
11
|
<slot :field="field" name="header">
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<template #content>
|
|
20
20
|
<slot :field="field" :name="`content-field-${field.name}`">
|
|
21
21
|
<slot :field="field" name="content">
|
|
22
|
-
<div :class="
|
|
22
|
+
<div :class="getContentClasses(field)" :data-cy="`grid-generator-${field.name}-result`" :title="getTitle(field, 'formattedResult')">
|
|
23
23
|
{{ field.formattedResult }}
|
|
24
24
|
</div>
|
|
25
25
|
</slot>
|
|
@@ -105,16 +105,6 @@ const hasResult = computed(() => Object.keys(props.result).length)
|
|
|
105
105
|
const hasFields = computed(() => Object.keys(props.fields).length)
|
|
106
106
|
const hasHeader = computed(() => Object.keys(props.headerProps).length)
|
|
107
107
|
|
|
108
|
-
const contentClass = computed(() => {
|
|
109
|
-
return [
|
|
110
|
-
props.contentClass,
|
|
111
|
-
|
|
112
|
-
{
|
|
113
|
-
ellipsis: !screen.isSmall && props.useEllipsis
|
|
114
|
-
}
|
|
115
|
-
]
|
|
116
|
-
})
|
|
117
|
-
|
|
118
108
|
const component = computed(() => {
|
|
119
109
|
return {
|
|
120
110
|
is: props.useBox ? 'qas-box' : 'div',
|
|
@@ -191,7 +181,7 @@ function setFieldsByResult () {
|
|
|
191
181
|
fieldsByResult.value = getFieldsByResult()
|
|
192
182
|
}
|
|
193
183
|
|
|
194
|
-
function
|
|
184
|
+
function getContainerClassses ({ key }) {
|
|
195
185
|
if (props.useInline) return 'row justify-between col-12'
|
|
196
186
|
|
|
197
187
|
return getFieldClass({ index: key, isGridGenerator: true })
|
|
@@ -200,4 +190,21 @@ function getContainerClass ({ key }) {
|
|
|
200
190
|
function getTitle (field, key) {
|
|
201
191
|
return props.useEllipsis ? field[key] : ''
|
|
202
192
|
}
|
|
193
|
+
|
|
194
|
+
function hasEllipsis (field) {
|
|
195
|
+
/**
|
|
196
|
+
* Para campos do tipo "textarea" vamos sempre exibir o conteúdo por completo.
|
|
197
|
+
*/
|
|
198
|
+
return (field.type === 'textarea') && !props.useInline ? false : props.useEllipsis
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function getContentClasses (field) {
|
|
202
|
+
return [
|
|
203
|
+
props.contentClass,
|
|
204
|
+
|
|
205
|
+
{
|
|
206
|
+
ellipsis: !screen.isSmall && this.hasEllipsis(field)
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
}
|
|
203
210
|
</script>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="containerClasses">
|
|
3
|
-
<div v-if="hasLabelSection" class="items-center justify-between no-wrap row" :class="labelSectionClasses">
|
|
3
|
+
<div v-if="hasLabelSection" class="full-width items-center justify-between no-wrap row" :class="labelSectionClasses">
|
|
4
4
|
<div class="items-center q-col-gutter-sm row">
|
|
5
5
|
<slot name="label">
|
|
6
6
|
<qas-label v-if="hasLabel" v-bind="defaultLabelProps" />
|
|
@@ -14,13 +14,15 @@
|
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
16
|
<slot name="actions">
|
|
17
|
-
<
|
|
17
|
+
<div class="q-mt-xs text-right">
|
|
18
|
+
<qas-actions-menu v-if="hasDefaultActionsMenu" v-bind="props.actionsMenuProps" />
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
<qas-btn v-if="hasDefaultButton" :use-label-on-small-screen="false" v-bind="props.buttonProps" />
|
|
21
|
+
</div>
|
|
20
22
|
</slot>
|
|
21
23
|
</div>
|
|
22
24
|
|
|
23
|
-
<div class="items-start
|
|
25
|
+
<div class="items-start no-wrap q-col-gutter-sm row" :class="descriptionSectionClasses">
|
|
24
26
|
<div v-if="hasDescriptionSection" class="text-body1 text-grey-8">
|
|
25
27
|
<slot name="description">
|
|
26
28
|
{{ props.description }}
|
|
@@ -90,13 +92,21 @@ const labelSectionClasses = computed(() => {
|
|
|
90
92
|
}
|
|
91
93
|
})
|
|
92
94
|
|
|
95
|
+
const descriptionSectionClasses = computed(() => {
|
|
96
|
+
return {
|
|
97
|
+
'justify-between': hasDescriptionSection.value,
|
|
98
|
+
'justify-end': hasActionsSection.value && !hasDescriptionSection.value
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
93
102
|
const defaultLabelProps = computed(() => {
|
|
94
103
|
return {
|
|
95
|
-
margin:
|
|
104
|
+
margin: 'none',
|
|
96
105
|
...props.labelProps
|
|
97
106
|
}
|
|
98
107
|
})
|
|
99
108
|
|
|
109
|
+
const hasActionsSection = computed(() => !!slots.actions || hasDefaultButton.value || hasDefaultActionsMenu.value)
|
|
100
110
|
const hasBadges = computed(() => !!props.badges.length)
|
|
101
111
|
const hasLabel = computed(() => !!Object.keys(props.labelProps).length)
|
|
102
112
|
const hasDefaultButton = computed(() => !!Object.keys(props.buttonProps).length)
|
|
@@ -2,24 +2,20 @@
|
|
|
2
2
|
<div :id="fieldName" class="qas-nested-fields" :data-cy="`nested-fields-${fieldName}`">
|
|
3
3
|
<component :is="containerComponent">
|
|
4
4
|
<div v-if="useSingleLabel" class="text-left">
|
|
5
|
-
<qas-label :label="fieldLabel"
|
|
5
|
+
<qas-label :label="fieldLabel" />
|
|
6
6
|
</div>
|
|
7
7
|
|
|
8
8
|
<div ref="inputContent">
|
|
9
9
|
<component :is="componentTag" v-bind="componentProps">
|
|
10
10
|
<template v-for="(row, index) in nested" :key="`row-${index}`">
|
|
11
11
|
<div v-if="!row[destroyKey]" :id="`row-${index}`" class="full-width qas-nested-fields__field-item" data-cy="nested-fields-item">
|
|
12
|
-
<header v-if="hasHeader" class="flex
|
|
13
|
-
<qas-label v-if="!useSingleLabel" :label="getRowLabel(index)" margin="none" typography="h5" />
|
|
14
|
-
|
|
15
|
-
<qas-actions-menu v-if="hasBlockActions(row)" v-bind="getActionsMenuProps(index, row)" :use-label="false" />
|
|
16
|
-
</header>
|
|
12
|
+
<qas-header v-if="hasHeader({ row })" class="flex" v-bind="getHeaderProps({ index, row })" />
|
|
17
13
|
|
|
18
14
|
<slot :errors="transformedErrors" :fields="getFields(index, row)" :index="index" :model="nested[index]" name="before-fields" :update-value="updateValuesFromInput" />
|
|
19
15
|
|
|
20
|
-
<div ref="formGenerator" class="
|
|
16
|
+
<div ref="formGenerator" :class="formGeneratorParentClasses">
|
|
21
17
|
<slot :errors="transformedErrors" :fields="getFields(index, row)" :index="index" name="fields" :update-value="updateValuesFromInput">
|
|
22
|
-
<qas-form-generator v-model="nested[index]" class="col" :columns="formColumns" :disable="isDisabledRow(row)" :errors="transformedErrors[index]" :fields="getFields(index, row)" :fields-props="getFieldsProps(index, row)" :gutter="formGutter" @update:model-value="updateValuesFromInput($event, index)">
|
|
18
|
+
<qas-form-generator v-model="nested[index]" class="col" :columns="formColumns" :common-columns="formCommonColumns" :disable="isDisabledRow(row)" :errors="transformedErrors[index]" :fields="getFields(index, row)" :fields-props="getFieldsProps(index, row)" :gutter="formGutter" @update:model-value="updateValuesFromInput($event, index)">
|
|
23
19
|
<template v-for="(slot, key) in $slots" #[key]="scope">
|
|
24
20
|
<slot v-bind="scope" :disabled="isDisabledRow(row)" :errors="transformedErrors" :index="index" :name="key" />
|
|
25
21
|
</template>
|
|
@@ -27,7 +23,7 @@
|
|
|
27
23
|
</slot>
|
|
28
24
|
|
|
29
25
|
<div v-if="hasInlineActions(row)" class="flex items-center qas-nested-fields__actions">
|
|
30
|
-
<qas-actions-menu v-bind="
|
|
26
|
+
<qas-actions-menu v-bind="getInlineActionsMenuProps(index, row)" :use-label="false" />
|
|
31
27
|
</div>
|
|
32
28
|
</div>
|
|
33
29
|
|
|
@@ -36,7 +32,7 @@
|
|
|
36
32
|
</template>
|
|
37
33
|
</component>
|
|
38
34
|
|
|
39
|
-
<div v-if="useAdd">
|
|
35
|
+
<div v-if="useAdd" :class="addButtonClass">
|
|
40
36
|
<slot :add="add" name="add-input">
|
|
41
37
|
<div v-if="showAddFirstInputButton" class="text-left">
|
|
42
38
|
<qas-btn class="q-px-sm" color="primary" data-cy="nested-fields-add-btn" :label="addFirstInputLabel" variant="tertiary" @click="add()" />
|
|
@@ -70,7 +66,7 @@ import QasInput from '../input/QasInput.vue'
|
|
|
70
66
|
import QasLabel from '../label/QasLabel.vue'
|
|
71
67
|
|
|
72
68
|
import { constructObject } from '../../helpers'
|
|
73
|
-
import { Spacing } from '../../enums/Spacing'
|
|
69
|
+
import { Spacing, SpacingWithNumber } from '../../enums/Spacing'
|
|
74
70
|
|
|
75
71
|
import { TransitionGroup } from 'vue'
|
|
76
72
|
import debug from 'debug'
|
|
@@ -167,12 +163,22 @@ export default {
|
|
|
167
163
|
default: () => []
|
|
168
164
|
},
|
|
169
165
|
|
|
166
|
+
formCommonColumns: {
|
|
167
|
+
type: [Object, String],
|
|
168
|
+
default: () => ({})
|
|
169
|
+
},
|
|
170
|
+
|
|
170
171
|
formGutter: {
|
|
171
|
-
default: Spacing.
|
|
172
|
+
default: Spacing.Md,
|
|
172
173
|
type: [String, Boolean],
|
|
173
174
|
validator: value => typeof value === 'boolean' || Object.values(Spacing).includes(value)
|
|
174
175
|
},
|
|
175
176
|
|
|
177
|
+
headerProps: {
|
|
178
|
+
type: Function,
|
|
179
|
+
default: () => {}
|
|
180
|
+
},
|
|
181
|
+
|
|
176
182
|
identifierItemKey: {
|
|
177
183
|
type: String,
|
|
178
184
|
default: 'uuid'
|
|
@@ -296,15 +302,14 @@ export default {
|
|
|
296
302
|
return this.useFirstInputButton && !this.nested.length
|
|
297
303
|
},
|
|
298
304
|
|
|
299
|
-
|
|
300
|
-
return (this.useSingleLabel && !this.useInlineActions) || !this.useSingleLabel
|
|
301
|
-
},
|
|
302
|
-
|
|
303
|
-
headerClasses () {
|
|
305
|
+
addButtonClass () {
|
|
304
306
|
return {
|
|
305
|
-
'
|
|
306
|
-
'justify-between': !this.useSingleLabel
|
|
307
|
+
'q-mt-md': !!this.nested.length
|
|
307
308
|
}
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
formGeneratorParentClasses () {
|
|
312
|
+
return this.useInlineActions ? 'col-12 justify-between q-col-gutter-x-md row' : 'full-width'
|
|
308
313
|
}
|
|
309
314
|
},
|
|
310
315
|
|
|
@@ -333,8 +338,8 @@ export default {
|
|
|
333
338
|
},
|
|
334
339
|
|
|
335
340
|
methods: {
|
|
336
|
-
|
|
337
|
-
if (typeof this.actionsMenuProps === 'function') {
|
|
341
|
+
getInlineActionsMenuProps (index, row) {
|
|
342
|
+
if (typeof this.actionsMenuProps === 'function' && this.useInlineActions) {
|
|
338
343
|
return this.actionsMenuProps({
|
|
339
344
|
index,
|
|
340
345
|
row,
|
|
@@ -344,7 +349,7 @@ export default {
|
|
|
344
349
|
|
|
345
350
|
return {
|
|
346
351
|
...this.actionsMenuProps,
|
|
347
|
-
list: this.getActionsMenuList(index, row)
|
|
352
|
+
list: this.getActionsMenuList(index, row, this.actionsMenuProps?.list)
|
|
348
353
|
}
|
|
349
354
|
},
|
|
350
355
|
|
|
@@ -368,11 +373,11 @@ export default {
|
|
|
368
373
|
return list
|
|
369
374
|
},
|
|
370
375
|
|
|
371
|
-
getActionsMenuList (index, row) {
|
|
376
|
+
getActionsMenuList (index, row, defaultList = {}) {
|
|
372
377
|
const list = this.getDefaultActionsMenuList(index, row)
|
|
373
378
|
|
|
374
|
-
for (const key in
|
|
375
|
-
const { handler, ...content } =
|
|
379
|
+
for (const key in defaultList) {
|
|
380
|
+
const { handler, ...content } = defaultList[key] || {}
|
|
376
381
|
|
|
377
382
|
list[key] = {
|
|
378
383
|
handler: payload => handler?.({ payload, row, index }),
|
|
@@ -456,14 +461,32 @@ export default {
|
|
|
456
461
|
|
|
457
462
|
setScroll () {
|
|
458
463
|
const elements = this.$refs.inputContent.children
|
|
464
|
+
|
|
465
|
+
// elemento de ação, e não das linhas (rows) de inputs
|
|
459
466
|
const element = elements[elements.length - 1]
|
|
467
|
+
|
|
468
|
+
// ultima linha (rows) de inputs
|
|
469
|
+
const rowsElement = elements[0]?.children
|
|
470
|
+
|
|
471
|
+
// pegamos a posição do elemento de ação
|
|
460
472
|
const { top } = element.getBoundingClientRect()
|
|
461
|
-
const pageOffset = window.pageYOffset
|
|
462
473
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
474
|
+
// pegamos a altura da ultima linha (rows) de inputs
|
|
475
|
+
const lastRowHeight = rowsElement?.[rowsElement.length - 1]?.clientHeight
|
|
476
|
+
|
|
477
|
+
// pegamos a posição da página
|
|
478
|
+
const pageOffset = window.scrollY
|
|
479
|
+
|
|
480
|
+
// 56 é a altura do header no mobile
|
|
481
|
+
const safeScrollSize = this.$qas.screen.isSmall ? 56 + SpacingWithNumber.Lg : SpacingWithNumber.Lg
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* É necessário descontar a altura da última linha (rows) de inputs para que o scroll
|
|
485
|
+
* fique no final da última linha (rows) de inputs.
|
|
486
|
+
*/
|
|
487
|
+
const scrollTop = pageOffset + top - (lastRowHeight + safeScrollSize)
|
|
488
|
+
|
|
489
|
+
window.scrollTo({ behavior: 'smooth', top: scrollTop })
|
|
467
490
|
},
|
|
468
491
|
|
|
469
492
|
async setFocus () {
|
|
@@ -507,6 +530,41 @@ export default {
|
|
|
507
530
|
|
|
508
531
|
hasInlineActions (row) {
|
|
509
532
|
return this.useInlineActions && !this.isDisabledRow(row)
|
|
533
|
+
},
|
|
534
|
+
|
|
535
|
+
hasHeader ({ row }) {
|
|
536
|
+
return this.hasBlockActions(row) || !this.useSingleLabel
|
|
537
|
+
},
|
|
538
|
+
|
|
539
|
+
getHeaderProps ({ index, row }) {
|
|
540
|
+
const hasLabel = !this.useSingleLabel
|
|
541
|
+
const hasActions = this.hasBlockActions(row)
|
|
542
|
+
|
|
543
|
+
const { labelProps, actionsMenuProps, ...payload } = this.headerProps?.({ index, row }) || {}
|
|
544
|
+
|
|
545
|
+
return {
|
|
546
|
+
...payload,
|
|
547
|
+
|
|
548
|
+
spacing: 'sm',
|
|
549
|
+
|
|
550
|
+
...(hasActions && {
|
|
551
|
+
actionsMenuProps: {
|
|
552
|
+
useLabel: false,
|
|
553
|
+
|
|
554
|
+
...actionsMenuProps,
|
|
555
|
+
|
|
556
|
+
list: this.getActionsMenuList(index, row, actionsMenuProps?.list)
|
|
557
|
+
}
|
|
558
|
+
}),
|
|
559
|
+
|
|
560
|
+
...(hasLabel && {
|
|
561
|
+
labelProps: {
|
|
562
|
+
typography: 'h5',
|
|
563
|
+
label: this.getRowLabel(index),
|
|
564
|
+
...labelProps
|
|
565
|
+
}
|
|
566
|
+
})
|
|
567
|
+
}
|
|
510
568
|
}
|
|
511
569
|
}
|
|
512
570
|
}
|
|
@@ -514,16 +572,13 @@ export default {
|
|
|
514
572
|
|
|
515
573
|
<style lang="scss">
|
|
516
574
|
.qas-nested-fields {
|
|
575
|
+
// mesmo tamanho do input
|
|
517
576
|
&__actions {
|
|
518
|
-
height:
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
&__field-item {
|
|
522
|
-
margin-bottom: var(--qas-spacing-md);
|
|
577
|
+
height: 40px;
|
|
523
578
|
}
|
|
524
579
|
|
|
525
580
|
&__field-item + &__field-item {
|
|
526
|
-
margin-top: var(--qas-spacing-
|
|
581
|
+
margin-top: var(--qas-spacing-lg);
|
|
527
582
|
}
|
|
528
583
|
}
|
|
529
584
|
</style>
|
|
@@ -74,12 +74,34 @@ props:
|
|
|
74
74
|
type: [Array, String, Object]
|
|
75
75
|
examples: ["[{ sm: 6, md: 12 }]", "{ name: { sm: 6, md: 12 } }", "12"]
|
|
76
76
|
|
|
77
|
+
form-common-columns:
|
|
78
|
+
desc: Colunas do grid comuns.
|
|
79
|
+
default: {}
|
|
80
|
+
type: [String, Object]
|
|
81
|
+
examples: ["{ col: 12, sm: 6}" , "12"]
|
|
82
|
+
|
|
77
83
|
form-gutter:
|
|
78
84
|
desc: Espaçamento entre colunas do formulário.
|
|
79
85
|
default: lg
|
|
80
86
|
type: [String, Boolean]
|
|
81
87
|
examples: [xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, false]
|
|
82
88
|
|
|
89
|
+
header-props:
|
|
90
|
+
desc: Propriedades do header do nested por linha por callback.
|
|
91
|
+
default: () => {}
|
|
92
|
+
type: Function
|
|
93
|
+
params:
|
|
94
|
+
context:
|
|
95
|
+
desc: Payload da linha (row) -> index/row.
|
|
96
|
+
type: Object
|
|
97
|
+
params:
|
|
98
|
+
index:
|
|
99
|
+
desc: Índice da linha.
|
|
100
|
+
type: Number
|
|
101
|
+
row:
|
|
102
|
+
desc: Valores contendo na linha atual.
|
|
103
|
+
type: Object
|
|
104
|
+
|
|
83
105
|
identifier-item-key:
|
|
84
106
|
desc: Define um identificador para o item. O identificador será utilizado para validar exclusão do item, por exemplo.
|
|
85
107
|
default: uuid
|
|
@@ -251,6 +273,7 @@ slots:
|
|
|
251
273
|
type: Function
|
|
252
274
|
examples: ["updateValue({ name: 'novo valor' }, 2)"]
|
|
253
275
|
|
|
276
|
+
|
|
254
277
|
events:
|
|
255
278
|
'@update:model-value -> function (value)':
|
|
256
279
|
desc: Dispara toda vez que o model é atualizado, também utilizado para v-model.
|
|
@@ -30,6 +30,34 @@
|
|
|
30
30
|
</qas-badge>
|
|
31
31
|
</template>
|
|
32
32
|
|
|
33
|
+
<template v-if="useCustomOptions" #option="scope">
|
|
34
|
+
<q-item v-bind="scope.itemProps" class="qas-select__option">
|
|
35
|
+
<q-item-section>
|
|
36
|
+
<div class="items-center q-gutter-x-sm row">
|
|
37
|
+
<q-item-label>
|
|
38
|
+
{{ scope.opt.label }}
|
|
39
|
+
</q-item-label>
|
|
40
|
+
|
|
41
|
+
<div v-for="(badge, index) in getFilteredBadgeList(scope.opt)" :key="index">
|
|
42
|
+
<qas-badge v-if="hasBadge(badge)" v-bind="getBadgeProps(badge)" />
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div v-if="scope.opt.caption">
|
|
47
|
+
<div class="items-center q-col-gutter-x-sm row">
|
|
48
|
+
<q-item-label v-for="(caption, index) in getCaptionArray(scope.opt.caption)" :key="index" caption class="items-center q-mt-xs row">
|
|
49
|
+
<div>
|
|
50
|
+
{{ caption }}
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<q-separator v-if="hasSeparator({ caption: getCaptionArray(scope.opt.caption), index })" class="q-ml-sm" vertical />
|
|
54
|
+
</q-item-label>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</q-item-section>
|
|
58
|
+
</q-item>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
33
61
|
<template v-for="(_, name) in $slots" #[name]="context">
|
|
34
62
|
<slot :name="name" v-bind="context || {}" />
|
|
35
63
|
</template>
|
|
@@ -50,6 +78,11 @@ export default {
|
|
|
50
78
|
mixins: [searchFilterMixin],
|
|
51
79
|
|
|
52
80
|
props: {
|
|
81
|
+
badgeProps: {
|
|
82
|
+
default: () => ({}),
|
|
83
|
+
type: Object
|
|
84
|
+
},
|
|
85
|
+
|
|
53
86
|
fuseOptions: {
|
|
54
87
|
default: () => ({}),
|
|
55
88
|
type: Object
|
|
@@ -83,6 +116,10 @@ export default {
|
|
|
83
116
|
type: Boolean
|
|
84
117
|
},
|
|
85
118
|
|
|
119
|
+
useCustomOptions: {
|
|
120
|
+
type: Boolean
|
|
121
|
+
},
|
|
122
|
+
|
|
86
123
|
useFetchOptionsOnCreate: {
|
|
87
124
|
default: true,
|
|
88
125
|
type: Boolean
|
|
@@ -328,6 +365,51 @@ export default {
|
|
|
328
365
|
: this.options[0]
|
|
329
366
|
|
|
330
367
|
this.$emit('update:modelValue', modelValue)
|
|
368
|
+
},
|
|
369
|
+
|
|
370
|
+
getFilteredBadgeList (payload = {}) {
|
|
371
|
+
const { label, value, disable, caption, ...rest } = payload
|
|
372
|
+
|
|
373
|
+
const badgeList = []
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Exemplo de estrutura percorrida:
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* {
|
|
380
|
+
* isTester: true,
|
|
381
|
+
* isOwner: false
|
|
382
|
+
* }
|
|
383
|
+
*/
|
|
384
|
+
for (const [key, val] of Object.entries(rest)) {
|
|
385
|
+
if (key in this.badgeProps) {
|
|
386
|
+
badgeList.push({ [key]: val })
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return badgeList
|
|
391
|
+
},
|
|
392
|
+
|
|
393
|
+
getBadgeProps (badge) {
|
|
394
|
+
const model = Object.keys(badge)[0]
|
|
395
|
+
|
|
396
|
+
const isFunction = typeof this.badgeProps[model] === 'function'
|
|
397
|
+
|
|
398
|
+
return isFunction ? this.badgeProps[model](badge[model]).props : this.badgeProps[model]
|
|
399
|
+
},
|
|
400
|
+
|
|
401
|
+
hasBadge (badge) {
|
|
402
|
+
const model = Object.keys(badge)[0]
|
|
403
|
+
|
|
404
|
+
return badge[model] || this.badgeProps[model](badge[model]).show
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
getCaptionArray (caption) {
|
|
408
|
+
return Array.isArray(caption) ? caption : [caption]
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
hasSeparator ({ caption, index }) {
|
|
412
|
+
return index !== caption.length - 1
|
|
331
413
|
}
|
|
332
414
|
}
|
|
333
415
|
}
|
|
@@ -353,6 +435,10 @@ export default {
|
|
|
353
435
|
}
|
|
354
436
|
}
|
|
355
437
|
|
|
438
|
+
&__option:hover .q-item__label--caption {
|
|
439
|
+
color: var(--q-primary);
|
|
440
|
+
}
|
|
441
|
+
|
|
356
442
|
&--closed {
|
|
357
443
|
.q-field__native span {
|
|
358
444
|
white-space: nowrap;
|
|
@@ -7,6 +7,12 @@ meta:
|
|
|
7
7
|
desc: Componente para select que implementa o "QSelect" repassando propriedades, slots e eventos.
|
|
8
8
|
|
|
9
9
|
props:
|
|
10
|
+
badge-list:
|
|
11
|
+
desc: Configuração das badges no qual cada key é um callback com o valor booleano retornado pelo back.
|
|
12
|
+
default: {}
|
|
13
|
+
examples: ["{ isTester: () => { return { color: 'grey-8', label: 'Tester', textColor: 'white' }} }"]
|
|
14
|
+
type: Object
|
|
15
|
+
|
|
10
16
|
entity:
|
|
11
17
|
desc: Entidade enviada para a action "fetchFieldOptions" (usar somente quando "useLazyLoading" estiver habilitada).
|
|
12
18
|
default: ''
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div ref="parent" :class="classes">
|
|
3
3
|
<div class="no-wrap row text-no-wrap">
|
|
4
4
|
<div ref="truncate" class="ellipsis">
|
|
5
|
-
<slot>{{
|
|
5
|
+
<slot>{{ formattedText }}</slot>
|
|
6
6
|
</div>
|
|
7
7
|
|
|
8
8
|
<qas-btn v-if="hasButton" class="q-ml-sm" :label="buttonLabel" @click.stop.prevent="toggle" />
|
|
@@ -87,6 +87,11 @@ const props = defineProps({
|
|
|
87
87
|
|
|
88
88
|
useObjectList: {
|
|
89
89
|
type: Boolean
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
emptyText: {
|
|
93
|
+
type: String,
|
|
94
|
+
default: '-'
|
|
90
95
|
}
|
|
91
96
|
})
|
|
92
97
|
|
|
@@ -119,6 +124,8 @@ useMutationObserver({ truncate, callbackFn: truncateText })
|
|
|
119
124
|
|
|
120
125
|
const classes = computed(() => [`text-${props.color}`, `text-${props.typography}`])
|
|
121
126
|
|
|
127
|
+
const formattedText = computed(() => props.list.length || props.text ? displayText.value : props.emptyText)
|
|
128
|
+
|
|
122
129
|
// composable functions
|
|
123
130
|
function useDialog ({ props, textContent }) {
|
|
124
131
|
// reactive vars
|
|
@@ -51,6 +51,10 @@ props:
|
|
|
51
51
|
desc: Utiliza a propriedade "list" como array de objeto contendo label (uso junto a prop list).
|
|
52
52
|
type: Boolean
|
|
53
53
|
|
|
54
|
+
empty-text:
|
|
55
|
+
desc: Texto a ser exibido no caso de não houver itens no "list" ou o "text" for vazio.
|
|
56
|
+
type: String
|
|
57
|
+
|
|
54
58
|
slots:
|
|
55
59
|
default:
|
|
56
60
|
desc: slot padrão que é utilizado para acessar o texto original (tanto o que é truncado quando o de dentro do dialog)
|
|
@@ -21,7 +21,7 @@ export const baseProps = {
|
|
|
21
21
|
},
|
|
22
22
|
|
|
23
23
|
gutter: {
|
|
24
|
-
default:
|
|
24
|
+
default: Spacing.Md,
|
|
25
25
|
type: [String, Boolean],
|
|
26
26
|
validator: gutterValidator
|
|
27
27
|
}
|
|
@@ -46,19 +46,13 @@ export default function ({ props = {} }) {
|
|
|
46
46
|
const classes = computed(() => {
|
|
47
47
|
const classesList = ['row']
|
|
48
48
|
|
|
49
|
-
if (
|
|
50
|
-
classesList.push(`q-col-gutter-${
|
|
49
|
+
if (props.gutter) {
|
|
50
|
+
classesList.push(`q-col-gutter-${props.gutter}`)
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
return classesList
|
|
54
54
|
})
|
|
55
55
|
|
|
56
|
-
const defaultGutter = computed(() => {
|
|
57
|
-
if (props.gutter) return props.gutter
|
|
58
|
-
|
|
59
|
-
return props.useInline ? Spacing.Md : Spacing.Lg
|
|
60
|
-
})
|
|
61
|
-
|
|
62
56
|
/**
|
|
63
57
|
* @function
|
|
64
58
|
* @param {Object} options
|
|
@@ -27,13 +27,17 @@
|
|
|
27
27
|
|
|
28
28
|
&--clickable:not(&--active) {
|
|
29
29
|
color: $grey-10;
|
|
30
|
-
transition:
|
|
30
|
+
transition: var(--qas-generic-transition);
|
|
31
31
|
|
|
32
32
|
&:not(&.q-router-link--active):hover {
|
|
33
33
|
color: var(--q-primary-contrast);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
&__label--caption {
|
|
38
|
+
transition: var(--qas-generic-transition);
|
|
39
|
+
}
|
|
40
|
+
|
|
37
41
|
&__section--avatar {
|
|
38
42
|
min-width: 0;
|
|
39
43
|
}
|
package/src/enums/Spacing.js
CHANGED
|
@@ -63,3 +63,36 @@ export const SpacingWithUnit = {
|
|
|
63
63
|
'4xl': '56px',
|
|
64
64
|
'5xl': '64px'
|
|
65
65
|
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Todas variáveis utilizadas para espaçamentos, com unidade de medida.
|
|
69
|
+
*
|
|
70
|
+
* @property
|
|
71
|
+
* @name SpacingWithUnit
|
|
72
|
+
* @readonly
|
|
73
|
+
* @enum
|
|
74
|
+
* @type {{
|
|
75
|
+
* None: 0,
|
|
76
|
+
* Xs: 4,
|
|
77
|
+
* Sm: 8,
|
|
78
|
+
* Md: 16,
|
|
79
|
+
* Lg: 24,
|
|
80
|
+
* Xl: 32,
|
|
81
|
+
* '2xl': 40,
|
|
82
|
+
* '3xl': 48,
|
|
83
|
+
* '4xl': 56,
|
|
84
|
+
* '5xl': 64
|
|
85
|
+
* }}
|
|
86
|
+
*/
|
|
87
|
+
export const SpacingWithNumber = {
|
|
88
|
+
None: 0,
|
|
89
|
+
Xs: 4,
|
|
90
|
+
Sm: 8,
|
|
91
|
+
Md: 16,
|
|
92
|
+
Lg: 24,
|
|
93
|
+
Xl: 32,
|
|
94
|
+
'2xl': 40,
|
|
95
|
+
'3xl': 48,
|
|
96
|
+
'4xl': 56,
|
|
97
|
+
'5xl': 64
|
|
98
|
+
}
|