@bildvitta/quasar-ui-asteroid 3.15.0-beta.1 → 3.15.0-beta.3
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/app-user/QasAppUser.vue +1 -1
- package/src/components/board-generator/QasBoardGenerator.vue +267 -0
- package/src/components/board-generator/QasBoardGenerator.yml +83 -0
- package/src/components/card/QasCard.vue +70 -59
- package/src/components/card/QasCard.yml +31 -33
- package/src/components/card-image/QasCardImage.vue +95 -0
- package/src/components/card-image/QasCardImage.yml +48 -0
- package/src/components/dialog-router/QasDialogRouter.yml +1 -1
- package/src/components/grabbable/QasGrabbable.vue +1 -20
- package/src/components/select-list-dialog/QasSelectListDialog.yml +3 -3
- package/src/components/signature-pad/QasSignaturePad.yml +4 -4
- package/src/css/utils/index.scss +1 -0
- package/src/css/utils/scroll.scss +21 -0
- package/src/vue-plugin.js +5 -0
package/package.json
CHANGED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<qas-grabbable class="qas-board-generator" use-scroll-bar>
|
|
3
|
+
<div class="no-wrap q-col-gutter-sm q-px-xl row">
|
|
4
|
+
<div v-for="(header, index) in results" :key="index" class="q-mr-sm">
|
|
5
|
+
<qas-box class="q-mb-md">
|
|
6
|
+
<slot :context="header" name="header-column" />
|
|
7
|
+
</qas-box>
|
|
8
|
+
|
|
9
|
+
<div ref="columnContainer" class="qas-board-generator__column secondary-scroll" :style="containerStyle">
|
|
10
|
+
<slot v-for="item in getItemsByHeader(header)" :context="item" name="column-item" />
|
|
11
|
+
|
|
12
|
+
<div class="full-width justify-center q-mb-md q-mt-sm row">
|
|
13
|
+
<qas-btn v-if="hasSeeMore(header)" icon="sym_r_add" label="Ver mais" :use-label-on-small-screen="false" variant="tertiary" @click="fetchColumn(header)" />
|
|
14
|
+
|
|
15
|
+
<q-spinner v-if="columnsLoading[getKeyByHeader(header)]" color="grey-4" size="3em" />
|
|
16
|
+
|
|
17
|
+
<qas-empty-result-text v-if="hasEmptyResultText(header)" />
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</qas-grabbable>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup>
|
|
26
|
+
import { ref, watch, computed, onMounted, shallowRef, inject } from 'vue'
|
|
27
|
+
import promiseHandler from '../../helpers/promise-handler'
|
|
28
|
+
|
|
29
|
+
defineOptions({ name: 'QasBoardGenerator' })
|
|
30
|
+
|
|
31
|
+
const columnContainer = ref(null)
|
|
32
|
+
const columnsPagination = ref({})
|
|
33
|
+
const columnsLoading = ref({})
|
|
34
|
+
|
|
35
|
+
const axios = inject('axios')
|
|
36
|
+
|
|
37
|
+
const props = defineProps({
|
|
38
|
+
results: {
|
|
39
|
+
type: Array,
|
|
40
|
+
default: () => []
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
columnIdKey: {
|
|
44
|
+
type: String,
|
|
45
|
+
required: true
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
columnParams: {
|
|
49
|
+
type: Object,
|
|
50
|
+
default: () => ({})
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
columnUrl: {
|
|
54
|
+
type: String,
|
|
55
|
+
required: true
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
modelValue: {
|
|
59
|
+
type: Object,
|
|
60
|
+
default: () => ({})
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
height: {
|
|
64
|
+
type: String,
|
|
65
|
+
default: ''
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
limitPerColumn: {
|
|
69
|
+
type: Number,
|
|
70
|
+
default: 12
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
columnWidth: {
|
|
74
|
+
type: String,
|
|
75
|
+
default: '288px'
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
useShallowRef: {
|
|
79
|
+
type: Boolean,
|
|
80
|
+
default: true
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
watch(
|
|
85
|
+
() => props.results,
|
|
86
|
+
() => {
|
|
87
|
+
reset()
|
|
88
|
+
setColumnHeightContainer()
|
|
89
|
+
setColumnsPagination()
|
|
90
|
+
fetchColumns()
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
watch(columnContainer, setColumnHeightContainer)
|
|
95
|
+
|
|
96
|
+
onMounted(() => {
|
|
97
|
+
setColumnsPagination()
|
|
98
|
+
fetchColumns()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const emit = defineEmits(['update:modelValue'])
|
|
102
|
+
|
|
103
|
+
defineExpose({ fetchColumns, fetchColumn, reset })
|
|
104
|
+
|
|
105
|
+
const columnsModel = computed({
|
|
106
|
+
get () {
|
|
107
|
+
return props.modelValue
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
set (newValues) {
|
|
111
|
+
emit('update:modelValue', newValues)
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const hasColumnsLength = computed(() => Object.keys(columnsModel.value).length)
|
|
116
|
+
|
|
117
|
+
const containerStyle = computed(() => `width: ${props.columnWidth};`)
|
|
118
|
+
|
|
119
|
+
/*
|
|
120
|
+
* Setar o tamanho do container do board, onde deverá ser a altura passada via prop, ou o default será ocupar o maximo
|
|
121
|
+
* de espaço que ele conseguir considerando a altura do container em relação ao topo.
|
|
122
|
+
*/
|
|
123
|
+
function setColumnHeightContainer () {
|
|
124
|
+
columnContainer.value?.forEach(columnElement => {
|
|
125
|
+
const heightToTop = columnElement?.getBoundingClientRect()?.top
|
|
126
|
+
const paddingSpacing = 60
|
|
127
|
+
const value = heightToTop + paddingSpacing
|
|
128
|
+
|
|
129
|
+
columnElement.style.setProperty('height', props.height ? props.height : `calc(100vh - ${value}px)`)
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/*
|
|
134
|
+
* Bater API pra cada header
|
|
135
|
+
*/
|
|
136
|
+
async function fetchColumns () {
|
|
137
|
+
props.results.forEach(header => fetchColumn(header))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/*
|
|
141
|
+
* Busca a coluna com base no header recebido.
|
|
142
|
+
*/
|
|
143
|
+
async function fetchColumn (header) {
|
|
144
|
+
const headerKey = getKeyByHeader(header)
|
|
145
|
+
const { limit, offset } = columnsPagination.value[headerKey] || {}
|
|
146
|
+
|
|
147
|
+
const { data: response, error } = await promiseHandler(
|
|
148
|
+
axios.get(`${props.columnUrl}/${headerKey}`, {
|
|
149
|
+
params: {
|
|
150
|
+
...props.columnParams,
|
|
151
|
+
limit,
|
|
152
|
+
offset
|
|
153
|
+
}
|
|
154
|
+
}),
|
|
155
|
+
{
|
|
156
|
+
onLoading: value => {
|
|
157
|
+
columnsLoading.value[headerKey] = value
|
|
158
|
+
},
|
|
159
|
+
useLoading: false,
|
|
160
|
+
errorMessage: 'Não conseguimos buscar as colunas do board. Por favor, tente novamente em alguns minutos.'
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
if (error) return
|
|
165
|
+
|
|
166
|
+
/*
|
|
167
|
+
* exemplo de como columnsModel irá ficar:
|
|
168
|
+
*
|
|
169
|
+
* {
|
|
170
|
+
* '2024-02-15': [...],
|
|
171
|
+
* '2024-02-16': [...]
|
|
172
|
+
* }
|
|
173
|
+
*
|
|
174
|
+
* onde cada item do objeto é uma coluna no board. O mesmo vale para "columnsLoading" e "columnPagination", organizando
|
|
175
|
+
* os loadings e o controle de paginação por chave identificadora do header.
|
|
176
|
+
*/
|
|
177
|
+
const newColumnValues = [
|
|
178
|
+
...columnsModel.value[headerKey] || [],
|
|
179
|
+
...response.data?.results || []
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
columnsModel.value[headerKey] = props.useShallowRef ? shallowRef(newColumnValues) : newColumnValues
|
|
183
|
+
|
|
184
|
+
columnsPagination.value[headerKey].offset = columnsModel.value[headerKey].length
|
|
185
|
+
columnsPagination.value[headerKey].count = response.data?.count
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function getItemsByHeader (header) {
|
|
189
|
+
return hasColumnsLength.value ? columnsModel.value[getKeyByHeader(header)] : []
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Pegar key com base na chave identificador, exemplo:
|
|
194
|
+
* header -> { date: '2024-02-12', ... }
|
|
195
|
+
* columnIdKey -> 'date'
|
|
196
|
+
* retorno -> '2024-02-12'
|
|
197
|
+
*
|
|
198
|
+
* Onde esta chave será o "id" da coluna, sendo usado para bater a API, lidar com paginação, loading, etc.
|
|
199
|
+
*
|
|
200
|
+
* @example getKeyByHeader({ date: '2024-02-12', ... })
|
|
201
|
+
* @returns {string} // '2024-02-12'
|
|
202
|
+
*/
|
|
203
|
+
function getKeyByHeader (header = {}) {
|
|
204
|
+
return header[props.columnIdKey]
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/*
|
|
208
|
+
* Para cada header, irá ser criado um item com sua chave identificadora para lidar com paginação e loading.
|
|
209
|
+
* columnsPagination -> { '2024-02-15': { limit: 12, offset: 0 }, '2024-02-16': { limit: 12, offset: 0 }, ... }
|
|
210
|
+
* columnsLoading ->{ '2024-02-15': false, '2024-02-16': false, ... }
|
|
211
|
+
*/
|
|
212
|
+
function setColumnsPagination () {
|
|
213
|
+
columnsPagination.value = {}
|
|
214
|
+
columnsLoading.value = {}
|
|
215
|
+
|
|
216
|
+
props.results.forEach(header => {
|
|
217
|
+
const headerKey = getKeyByHeader(header)
|
|
218
|
+
|
|
219
|
+
columnsPagination.value[headerKey] = { limit: props.limitPerColumn, offset: 0 }
|
|
220
|
+
columnsLoading.value[headerKey] = false
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function hasEmptyResultText (header) {
|
|
225
|
+
return !columnsLoading.value[getKeyByHeader(header)] && !getItemsByHeader(header)?.length
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/*
|
|
229
|
+
* Valida se o tamanho dos itens da coluna é menor que o valor total de itens que o back retorna e
|
|
230
|
+
* se a coluna não está em carregamento.
|
|
231
|
+
*/
|
|
232
|
+
function hasSeeMore (header) {
|
|
233
|
+
const headerKey = getKeyByHeader(header)
|
|
234
|
+
const hasMorePagination = columnsModel.value[headerKey]?.length < columnsPagination.value[headerKey]?.count
|
|
235
|
+
|
|
236
|
+
return hasMorePagination && !columnsLoading.value[headerKey]
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function reset () {
|
|
240
|
+
columnsModel.value = {}
|
|
241
|
+
columnsPagination.value = {}
|
|
242
|
+
columnsLoading.value = {}
|
|
243
|
+
}
|
|
244
|
+
</script>
|
|
245
|
+
|
|
246
|
+
<style lang="scss">
|
|
247
|
+
.qas-board-generator {
|
|
248
|
+
max-height: 100vh;
|
|
249
|
+
|
|
250
|
+
&__column {
|
|
251
|
+
overflow-x: hidden;
|
|
252
|
+
scrollbar-width: none;
|
|
253
|
+
|
|
254
|
+
&:hover {
|
|
255
|
+
scrollbar-width: thin;
|
|
256
|
+
|
|
257
|
+
&::-webkit-scrollbar {
|
|
258
|
+
display: block;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
&::-webkit-scrollbar {
|
|
263
|
+
display: none;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
</style>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
type: component
|
|
2
|
+
|
|
3
|
+
meta:
|
|
4
|
+
desc: Componente usado para board de colunas.
|
|
5
|
+
|
|
6
|
+
props:
|
|
7
|
+
results:
|
|
8
|
+
desc: Lista de itens sendo cada um o header de cada coluna.
|
|
9
|
+
default: []
|
|
10
|
+
type: Array
|
|
11
|
+
|
|
12
|
+
column-id-key:
|
|
13
|
+
desc: chave que será o id usado para ser o identificador da coluna.
|
|
14
|
+
type: String
|
|
15
|
+
required: true
|
|
16
|
+
|
|
17
|
+
column-params:
|
|
18
|
+
desc: parâmetros customizados caso precise passar para as APIs das colunas.
|
|
19
|
+
type: Object
|
|
20
|
+
default: {}
|
|
21
|
+
|
|
22
|
+
column-url:
|
|
23
|
+
desc: URL usada para as APIs das colunas.
|
|
24
|
+
type: String
|
|
25
|
+
required: true
|
|
26
|
+
|
|
27
|
+
column-width:
|
|
28
|
+
desc: Largura da coluna
|
|
29
|
+
type: String
|
|
30
|
+
default: 288
|
|
31
|
+
|
|
32
|
+
height:
|
|
33
|
+
desc: Altura do container do board. Caso não seja passado um height, o calculo é feito para ocupar a maior altura possível da página.
|
|
34
|
+
type: String
|
|
35
|
+
|
|
36
|
+
limit:
|
|
37
|
+
desc: Quantidade de itens da coluna por busca na API.
|
|
38
|
+
type: Number
|
|
39
|
+
default: 12
|
|
40
|
+
|
|
41
|
+
use-shallow-ref:
|
|
42
|
+
desc: Define se os valores dos itens das colunas irá ser atribuido utilizando o "shallowRef", onde somente a primeira camada do model será reativa.(https://vuejs.org/api/reactivity-advanced.html#shallowref)
|
|
43
|
+
type: Boolean
|
|
44
|
+
default: true
|
|
45
|
+
|
|
46
|
+
model-value:
|
|
47
|
+
desc: Model do componente, usado para o v-model.
|
|
48
|
+
default: {}
|
|
49
|
+
type: Object
|
|
50
|
+
examples: [v-model="value"]
|
|
51
|
+
model: true
|
|
52
|
+
|
|
53
|
+
slots:
|
|
54
|
+
header-column:
|
|
55
|
+
desc: Slot para acessar o card do header de cada coluna.
|
|
56
|
+
scope:
|
|
57
|
+
context:
|
|
58
|
+
desc: Informações de cada item do header que foi fornecido através do "results".
|
|
59
|
+
type: Object
|
|
60
|
+
default: {}
|
|
61
|
+
|
|
62
|
+
column-item:
|
|
63
|
+
desc: Slot para acessar o item da coluna.
|
|
64
|
+
scope:
|
|
65
|
+
context:
|
|
66
|
+
desc: Informações de cada item da coluna que foi buscado através da API.
|
|
67
|
+
type: Object
|
|
68
|
+
default: {}
|
|
69
|
+
|
|
70
|
+
events:
|
|
71
|
+
'@update:model-value -> function(value)':
|
|
72
|
+
desc: Dispara quando o model-value altera, também usado para v-model.
|
|
73
|
+
params:
|
|
74
|
+
value:
|
|
75
|
+
desc: Novo valor do model.
|
|
76
|
+
type: Object
|
|
77
|
+
|
|
78
|
+
methods:
|
|
79
|
+
'fetchColumns: () => void':
|
|
80
|
+
desc: Busca todas colunas com base nos headers fornecidos.
|
|
81
|
+
|
|
82
|
+
'fetchColumn: (header) => void':
|
|
83
|
+
desc: Busca uma coluna especifica com base no header fornecido.
|
|
@@ -1,95 +1,106 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="
|
|
3
|
-
<q-card class="
|
|
4
|
-
<
|
|
5
|
-
<
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
</slot>
|
|
18
|
-
</header>
|
|
2
|
+
<div class="qas-card">
|
|
3
|
+
<q-card class="column full-height overflow-hidden q-px-md q-py-sm rounded-borders-right rounded-borders-sm shadow-2" :style="style">
|
|
4
|
+
<div class="items-center justify-between row">
|
|
5
|
+
<component :is="titleComponent" class="text-h4 text-no-decoration" :class="titleClasses" :to="route">
|
|
6
|
+
<slot name="title">
|
|
7
|
+
{{ props.title }}
|
|
8
|
+
</slot>
|
|
9
|
+
</component>
|
|
10
|
+
|
|
11
|
+
<qas-actions-menu v-if="hasActions" :list="actionsMenuProps" :use-label="false" />
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div class="q-my-sm">
|
|
15
|
+
<slot name="default" />
|
|
16
|
+
</div>
|
|
19
17
|
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
<slot />
|
|
23
|
-
</div>
|
|
24
|
-
</q-card-section>
|
|
18
|
+
<div class="q-mt-auto">
|
|
19
|
+
<q-separator v-if="hasFooter" class="q-mb-sm" />
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
<slot name="footer">
|
|
22
|
+
<q-expansion-item v-if="hasExpansion" class="full-width" dense expand-icon-class="text-primary" header-class="q-pa-none text-primary" :label="props.expansionProps.label">
|
|
23
|
+
<slot name="expansion-content">
|
|
24
|
+
{{ props.expansionProps.content }}
|
|
25
|
+
</slot>
|
|
26
|
+
</q-expansion-item>
|
|
27
|
+
</slot>
|
|
28
28
|
</div>
|
|
29
29
|
</q-card>
|
|
30
30
|
</div>
|
|
31
31
|
</template>
|
|
32
32
|
|
|
33
33
|
<script setup>
|
|
34
|
-
import {
|
|
34
|
+
import { computed, useSlots } from 'vue'
|
|
35
35
|
|
|
36
|
-
import {
|
|
36
|
+
import { colors } from 'quasar'
|
|
37
37
|
|
|
38
38
|
defineOptions({ name: 'QasCard' })
|
|
39
39
|
|
|
40
40
|
const props = defineProps({
|
|
41
|
-
|
|
42
|
-
type:
|
|
43
|
-
default:
|
|
41
|
+
actionsMenuProps: {
|
|
42
|
+
type: Object,
|
|
43
|
+
default: () => ({})
|
|
44
44
|
},
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
type:
|
|
48
|
-
default:
|
|
49
|
-
validator: value => Object.values(Spacing).includes(value)
|
|
46
|
+
expansionProps: {
|
|
47
|
+
type: Object,
|
|
48
|
+
default: () => ({})
|
|
50
49
|
},
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
route: {
|
|
52
|
+
type: Object,
|
|
53
|
+
default: () => ({})
|
|
55
54
|
},
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
type:
|
|
56
|
+
statusColor: {
|
|
57
|
+
type: String,
|
|
58
|
+
default: ''
|
|
59
59
|
},
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
type:
|
|
63
|
-
|
|
61
|
+
title: {
|
|
62
|
+
type: String,
|
|
63
|
+
default: ''
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const hasActions = computed(() => !!Object.keys(props.actionsMenuProps).length)
|
|
68
|
+
|
|
69
|
+
const hasExpansion = computed(() => !!Object.keys(props.expansionProps).length)
|
|
64
70
|
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
const hasRoute = computed(() => !!Object.keys(props.route).length)
|
|
72
|
+
|
|
73
|
+
const titleClasses = computed(() => {
|
|
74
|
+
return {
|
|
75
|
+
'qas-card__router ': hasRoute.value
|
|
67
76
|
}
|
|
68
77
|
})
|
|
69
78
|
|
|
70
|
-
const
|
|
79
|
+
const titleComponent = computed(() => hasRoute.value ? 'router-link' : 'div')
|
|
71
80
|
|
|
72
|
-
const
|
|
81
|
+
const style = computed(() => {
|
|
82
|
+
if (!props.statusColor) return
|
|
83
|
+
|
|
84
|
+
const { getPaletteColor } = colors
|
|
73
85
|
|
|
74
|
-
const cardClasses = computed(() => {
|
|
75
86
|
return {
|
|
76
|
-
|
|
77
|
-
'border-primary': props.outlined,
|
|
78
|
-
'no-shadow': props.outlined,
|
|
79
|
-
'bg-white': props.outlined
|
|
87
|
+
borderLeft: `4px solid ${getPaletteColor(props.statusColor)}`
|
|
80
88
|
}
|
|
81
89
|
})
|
|
82
90
|
|
|
83
|
-
const
|
|
84
|
-
const gutterClass = computed(() => `q-col-gutter-${props.gutter}`)
|
|
91
|
+
const slots = useSlots()
|
|
85
92
|
|
|
86
|
-
const
|
|
87
|
-
const hasImages = computed(() => props.images.length > 1)
|
|
93
|
+
const hasFooterSlot = computed(() => !!slots.footer)
|
|
88
94
|
|
|
89
|
-
const
|
|
90
|
-
|
|
95
|
+
const hasFooter = computed(() => hasFooterSlot.value || hasExpansion.value)
|
|
96
|
+
</script>
|
|
91
97
|
|
|
92
|
-
|
|
93
|
-
|
|
98
|
+
<style lang="scss">
|
|
99
|
+
.qas-card {
|
|
100
|
+
&__router {
|
|
101
|
+
&:hover {
|
|
102
|
+
color: $primary;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
94
105
|
}
|
|
95
|
-
</
|
|
106
|
+
</style>
|
|
@@ -4,43 +4,41 @@ meta:
|
|
|
4
4
|
desc: Componente de card.
|
|
5
5
|
|
|
6
6
|
props:
|
|
7
|
-
|
|
8
|
-
desc:
|
|
9
|
-
default:
|
|
7
|
+
actions-menu-props:
|
|
8
|
+
desc: Propriedades repassadas para o QasActionsMenu.
|
|
9
|
+
default: {}
|
|
10
|
+
type: Object
|
|
11
|
+
|
|
12
|
+
expansion-props:
|
|
13
|
+
desc: Conteúdo que ficará dentro do "QExpansionItem", sendo separado por "label" e "content".
|
|
14
|
+
type: Object
|
|
15
|
+
default: {}
|
|
16
|
+
|
|
17
|
+
route:
|
|
18
|
+
desc: Rota que será utilizada ao clicar no título.
|
|
19
|
+
default: {}
|
|
20
|
+
type: Object
|
|
21
|
+
|
|
22
|
+
status-color:
|
|
23
|
+
desc: Insere uma borda na esquerda para indicar o status do card.
|
|
24
|
+
default: ''
|
|
25
|
+
examples: [red-14, blue-8, green-9]
|
|
10
26
|
type: String
|
|
11
|
-
examples: [xs, sm, md, lg, xl]
|
|
12
27
|
|
|
13
|
-
|
|
14
|
-
desc:
|
|
15
|
-
default:
|
|
16
|
-
type:
|
|
17
|
-
|
|
18
|
-
image-position:
|
|
19
|
-
desc: Posição da imagem "background-position".
|
|
20
|
-
default: center
|
|
21
|
-
type: String
|
|
22
|
-
|
|
23
|
-
outlined:
|
|
24
|
-
desc: Insere uma borda sólida em volta do componente.
|
|
25
|
-
type: Boolean
|
|
26
|
-
|
|
27
|
-
unelevated:
|
|
28
|
-
desc: Remove a sombra do componente.
|
|
29
|
-
type: Boolean
|
|
30
|
-
|
|
31
|
-
use-header:
|
|
32
|
-
desc: Controla se vai ter ou não o header com carousel.
|
|
33
|
-
type: Boolean
|
|
28
|
+
title:
|
|
29
|
+
desc: Título principal do card.
|
|
30
|
+
default: ''
|
|
31
|
+
type: String
|
|
34
32
|
|
|
35
33
|
slots:
|
|
36
|
-
|
|
37
|
-
desc: Slot para acessar conteúdo
|
|
34
|
+
default:
|
|
35
|
+
desc: Slot para acessar conteúdo principal do card.
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
desc: Slot para acessar
|
|
37
|
+
title:
|
|
38
|
+
desc: Slot para acessar conteúdo do título do card.
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
desc: Slot para acessar
|
|
40
|
+
footer:
|
|
41
|
+
desc: Slot para acessar o footer do card.
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
desc: Slot para acessar
|
|
43
|
+
expansion-content:
|
|
44
|
+
desc: Slot para acessar o conteúdo dentro do "QExpansionItem".
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="col-12 col-lg-3 col-md-4 col-sm-6">
|
|
3
|
+
<q-card class="border-radius-lg column full-height overflow-hidden" :class="cardClasses">
|
|
4
|
+
<header v-if="props.useHeader" class="full-width overflow-hidden relative-position">
|
|
5
|
+
<slot name="header">
|
|
6
|
+
<q-carousel v-model="slideImage" animated class="cursor-pointer" height="205px" infinite :navigation="hasImages" navigation-icon="sym_r_fiber_manual_record" swipeable>
|
|
7
|
+
<template #navigation-icon="{ active, btnProps, onClick }">
|
|
8
|
+
<qas-btn color="white" :icon="getNavigationIcon(active, btnProps)" variant="tertiary" @click="onClick" />
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<q-carousel-slide v-for="(item, index) in imagesList" :key="index" class="bg-no-repeat" :class="imagePositionClass" :img-src="item" :name="index" />
|
|
12
|
+
</q-carousel>
|
|
13
|
+
|
|
14
|
+
<div class="absolute-top flex items-center q-pa-md">
|
|
15
|
+
<slot name="carousel-header" />
|
|
16
|
+
</div>
|
|
17
|
+
</slot>
|
|
18
|
+
</header>
|
|
19
|
+
|
|
20
|
+
<q-card-section class="col-grow column full-width justify-between">
|
|
21
|
+
<div class="full-width" :class="gutterClass">
|
|
22
|
+
<slot />
|
|
23
|
+
</div>
|
|
24
|
+
</q-card-section>
|
|
25
|
+
|
|
26
|
+
<div v-if="hasActionsSlot" class="border-grey border-top overflow-hidden row">
|
|
27
|
+
<slot name="actions" />
|
|
28
|
+
</div>
|
|
29
|
+
</q-card>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script setup>
|
|
34
|
+
import { Spacing } from '../../enums/Spacing'
|
|
35
|
+
|
|
36
|
+
import { ref, computed, useSlots } from 'vue'
|
|
37
|
+
|
|
38
|
+
defineOptions({ name: 'QasCardImage' })
|
|
39
|
+
|
|
40
|
+
const props = defineProps({
|
|
41
|
+
imagePosition: {
|
|
42
|
+
type: String,
|
|
43
|
+
default: 'center'
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
gutter: {
|
|
47
|
+
type: String,
|
|
48
|
+
default: Spacing.Sm,
|
|
49
|
+
validator: value => Object.values(Spacing).includes(value)
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
images: {
|
|
53
|
+
default: () => [],
|
|
54
|
+
type: Array
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
outlined: {
|
|
58
|
+
type: Boolean
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
unelevated: {
|
|
62
|
+
type: Boolean
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
useHeader: {
|
|
66
|
+
type: Boolean
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const slots = useSlots()
|
|
71
|
+
|
|
72
|
+
const slideImage = ref(0)
|
|
73
|
+
|
|
74
|
+
const cardClasses = computed(() => {
|
|
75
|
+
return {
|
|
76
|
+
'shadow-2': !props.unelevated,
|
|
77
|
+
'border-primary': props.outlined,
|
|
78
|
+
'no-shadow': props.outlined,
|
|
79
|
+
'bg-white': props.outlined
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const imagePositionClass = computed(() => `bg-position-${props.imagePosition}`)
|
|
84
|
+
const gutterClass = computed(() => `q-col-gutter-${props.gutter}`)
|
|
85
|
+
|
|
86
|
+
const hasActionsSlot = computed(() => !!slots.actions)
|
|
87
|
+
const hasImages = computed(() => props.images.length > 1)
|
|
88
|
+
|
|
89
|
+
const imagesLength = computed(() => props.images?.length)
|
|
90
|
+
const imagesList = computed(() => imagesLength.value && props.images.slice(0, 3))
|
|
91
|
+
|
|
92
|
+
function getNavigationIcon (active, { icon }) {
|
|
93
|
+
return active ? 'sym_r_radio_button_checked' : icon
|
|
94
|
+
}
|
|
95
|
+
</script>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
type: component
|
|
2
|
+
|
|
3
|
+
meta:
|
|
4
|
+
desc: Componente de card utilizando imagens.
|
|
5
|
+
|
|
6
|
+
props:
|
|
7
|
+
gutter:
|
|
8
|
+
desc: Espaçamento dentro <q-card-section /> onde fica o slot "default".
|
|
9
|
+
default: sm
|
|
10
|
+
type: String
|
|
11
|
+
examples: [xs, sm, md, lg, xl]
|
|
12
|
+
|
|
13
|
+
images:
|
|
14
|
+
desc: Imagens do carousel.
|
|
15
|
+
default: []
|
|
16
|
+
type: Array
|
|
17
|
+
|
|
18
|
+
image-position:
|
|
19
|
+
desc: Posição da imagem "background-position".
|
|
20
|
+
default: center
|
|
21
|
+
type: String
|
|
22
|
+
|
|
23
|
+
outlined:
|
|
24
|
+
desc: Insere uma borda sólida em volta do componente.
|
|
25
|
+
type: Boolean
|
|
26
|
+
|
|
27
|
+
unelevated:
|
|
28
|
+
desc: Remove a sombra do componente.
|
|
29
|
+
type: Boolean
|
|
30
|
+
|
|
31
|
+
use-header:
|
|
32
|
+
desc: Controla se vai ter ou não o header com carousel.
|
|
33
|
+
type: Boolean
|
|
34
|
+
|
|
35
|
+
slots:
|
|
36
|
+
actions:
|
|
37
|
+
desc: Slot para acessar conteúdo de ações como botão.
|
|
38
|
+
|
|
39
|
+
carousel-header:
|
|
40
|
+
desc: Slot para acessar usar de header do "<q-carousel />".
|
|
41
|
+
|
|
42
|
+
default:
|
|
43
|
+
desc: Slot para acessar conteúdo dentro do "<q-card-section />".
|
|
44
|
+
|
|
45
|
+
header:
|
|
46
|
+
desc: Slot para acessar área no lugar do "<q-carousel />".
|
|
47
|
+
|
|
48
|
+
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="qas-grabbable relative-position">
|
|
3
3
|
<div
|
|
4
4
|
ref="grabContainer"
|
|
5
|
-
class="flex no-wrap qas-grabbable__container"
|
|
5
|
+
class="flex no-wrap qas-grabbable__container secondary-scroll"
|
|
6
6
|
:class="classes"
|
|
7
7
|
>
|
|
8
8
|
<slot />
|
|
@@ -136,25 +136,6 @@ onBeforeUnmount(() => {
|
|
|
136
136
|
-webkit-overflow-scrolling: touch;
|
|
137
137
|
-ms-overflow-style: none;
|
|
138
138
|
overflow-x: auto;
|
|
139
|
-
scrollbar-color: $blue-grey-3 transparent;
|
|
140
|
-
|
|
141
|
-
&::-webkit-scrollbar {
|
|
142
|
-
height: 12px;
|
|
143
|
-
background-color: transparent;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
&::-webkit-scrollbar-track {
|
|
147
|
-
background-color: transparent;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
&::-webkit-scrollbar-thumb {
|
|
151
|
-
background-color: $blue-grey-3;
|
|
152
|
-
border-radius: var(--qas-generic-border-radius);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
&::-webkit-scrollbar-thumb:hover {
|
|
156
|
-
background-color: $blue-grey-4;
|
|
157
|
-
}
|
|
158
139
|
|
|
159
140
|
&::before,
|
|
160
141
|
&::after {
|
|
@@ -98,11 +98,11 @@ events:
|
|
|
98
98
|
type: Array
|
|
99
99
|
|
|
100
100
|
methods:
|
|
101
|
-
'add ({ options = [], value }) =>
|
|
101
|
+
'add: ({ options = [], value }) => void':
|
|
102
102
|
desc: Adiciona itens a lista de selecionados, é preciso adicionar quais opções são referentes ao model.
|
|
103
103
|
|
|
104
|
-
'remove (value) =>
|
|
104
|
+
'remove: (value) => void':
|
|
105
105
|
desc: remove um item da lista de selecionados passando o valor referente a ele na lista.
|
|
106
106
|
|
|
107
|
-
'removeAll () =>
|
|
107
|
+
'removeAll: () => void':
|
|
108
108
|
desc: remove todos os itens da lista de selecionados.
|
|
@@ -29,11 +29,11 @@ slots:
|
|
|
29
29
|
default:
|
|
30
30
|
desc: Usado para conseguir recuperar as função internas do componente.
|
|
31
31
|
scope:
|
|
32
|
-
'clearSignature () =>
|
|
32
|
+
'clearSignature: () => void':
|
|
33
33
|
desc: Limpa a assinatura.
|
|
34
34
|
type: Function
|
|
35
35
|
|
|
36
|
-
'getSignatureData () => String':
|
|
36
|
+
'getSignatureData: () => String':
|
|
37
37
|
desc: 'Obtém a assinatura em formato de imagem com "tipo" de acordo com o que foi passado para o componente (ex: "image/png"). Utiliza o HTMLCanvasElement.toDataURL()'
|
|
38
38
|
type: Function
|
|
39
39
|
|
|
@@ -46,8 +46,8 @@ events:
|
|
|
46
46
|
type: Boolean
|
|
47
47
|
|
|
48
48
|
methods:
|
|
49
|
-
'clearSignature () =>
|
|
49
|
+
'clearSignature: () => void':
|
|
50
50
|
desc: Limpa a assinatura.
|
|
51
51
|
|
|
52
|
-
'getSignatureData () => String':
|
|
52
|
+
'getSignatureData: () => String':
|
|
53
53
|
desc: 'Obtém a assinatura em formato de imagem com "tipo" de acordo com o que foi passado para o componente (ex: "image/png"). Utiliza o HTMLCanvasElement.toDataURL()'
|
package/src/css/utils/index.scss
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.secondary-scroll {
|
|
2
|
+
scrollbar-color: $blue-grey-3 transparent;
|
|
3
|
+
|
|
4
|
+
&::-webkit-scrollbar {
|
|
5
|
+
height: 12px;
|
|
6
|
+
background-color: transparent;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
&::-webkit-scrollbar-track {
|
|
10
|
+
background-color: transparent;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
&::-webkit-scrollbar-thumb {
|
|
14
|
+
background-color: $blue-grey-3;
|
|
15
|
+
border-radius: var(--qas-generic-border-radius);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&::-webkit-scrollbar-thumb:hover {
|
|
19
|
+
background-color: $blue-grey-4;
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/vue-plugin.js
CHANGED
|
@@ -6,11 +6,13 @@ import QasAppMenu from './components/app-menu/QasAppMenu.vue'
|
|
|
6
6
|
import QasAppUser from './components/app-user/QasAppUser.vue'
|
|
7
7
|
import QasAvatar from './components/avatar/QasAvatar.vue'
|
|
8
8
|
import QasBadge from './components/badge/QasBadge.vue'
|
|
9
|
+
import QasBoardGenerator from './components/board-generator/QasBoardGenerator.vue'
|
|
9
10
|
import QasBox from './components/box/QasBox.vue'
|
|
10
11
|
import QasBreakline from './components/breakline/QasBreakline.vue'
|
|
11
12
|
import QasBtn from './components/btn/QasBtn.vue'
|
|
12
13
|
import QasBtnDropdown from './components/btn-dropdown/QasBtnDropdown.vue'
|
|
13
14
|
import QasCard from './components/card/QasCard.vue'
|
|
15
|
+
import QasCardImage from './components/card-image/QasCardImage.vue'
|
|
14
16
|
import QasCheckboxGroup from './components/checkbox-group/QasCheckboxGroup.vue'
|
|
15
17
|
import QasCopy from './components/copy/QasCopy.vue'
|
|
16
18
|
import QasDate from './components/date/QasDate.vue'
|
|
@@ -94,11 +96,13 @@ async function install (app) {
|
|
|
94
96
|
app.component('QasAppUser', QasAppUser)
|
|
95
97
|
app.component('QasAvatar', QasAvatar)
|
|
96
98
|
app.component('QasBadge', QasBadge)
|
|
99
|
+
app.component('QasBoardGenerator', QasBoardGenerator)
|
|
97
100
|
app.component('QasBox', QasBox)
|
|
98
101
|
app.component('QasBreakline', QasBreakline)
|
|
99
102
|
app.component('QasBtn', QasBtn)
|
|
100
103
|
app.component('QasBtnDropdown', QasBtnDropdown)
|
|
101
104
|
app.component('QasCard', QasCard)
|
|
105
|
+
app.component('QasCardImage', QasCardImage)
|
|
102
106
|
app.component('QasCheckboxGroup', QasCheckboxGroup)
|
|
103
107
|
app.component('QasCopy', QasCopy)
|
|
104
108
|
app.component('QasDate', QasDate)
|
|
@@ -184,6 +188,7 @@ export {
|
|
|
184
188
|
QasAppUser,
|
|
185
189
|
QasAvatar,
|
|
186
190
|
QasBadge,
|
|
191
|
+
QasBoardGenerator,
|
|
187
192
|
QasBox,
|
|
188
193
|
QasBreakline,
|
|
189
194
|
QasBtn,
|