@bildvitta/quasar-ui-asteroid 3.12.0-beta.0 → 3.12.0-beta.2
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/filters/QasFilters.vue +8 -15
- package/src/components/search-box/QasSearchBox.vue +1 -1
- package/src/components/select-list-dialog/QasSelectListDialog.vue +386 -0
- package/src/components/select-list-dialog/QasSelectListDialog.yml +108 -0
- package/src/components/timeline/QasTimeline.vue +140 -0
- package/src/components/timeline/QasTimeline.yml +36 -0
- package/src/composables/index.js +3 -1
- package/src/composables/use-context.js +15 -0
- package/src/composables/use-query-cache.js +53 -0
- package/src/vue-plugin.js +9 -3
package/package.json
CHANGED
|
@@ -224,9 +224,14 @@ export default {
|
|
|
224
224
|
}
|
|
225
225
|
},
|
|
226
226
|
|
|
227
|
-
created () {
|
|
228
|
-
this.fetchFilters()
|
|
229
|
-
|
|
227
|
+
async created () {
|
|
228
|
+
await this.fetchFilters()
|
|
229
|
+
|
|
230
|
+
if (this.useUpdateRoute) {
|
|
231
|
+
this.updateValues()
|
|
232
|
+
this.updateCurrentFilters()
|
|
233
|
+
}
|
|
234
|
+
|
|
230
235
|
this.handleSearchModelOnCreate()
|
|
231
236
|
},
|
|
232
237
|
|
|
@@ -371,18 +376,6 @@ export default {
|
|
|
371
376
|
return isMultiple ? [value] : value
|
|
372
377
|
},
|
|
373
378
|
|
|
374
|
-
watchOnceFields () {
|
|
375
|
-
if (!this.useUpdateRoute) return
|
|
376
|
-
|
|
377
|
-
const watchOnce = this.$watch('fields', values => {
|
|
378
|
-
if (Object.keys(values || {}).length) {
|
|
379
|
-
this.updateValues()
|
|
380
|
-
this.updateCurrentFilters()
|
|
381
|
-
watchOnce()
|
|
382
|
-
}
|
|
383
|
-
})
|
|
384
|
-
},
|
|
385
|
-
|
|
386
379
|
handleSearchModelOnCreate () {
|
|
387
380
|
if (this.useUpdateRoute && !this.useFilterButton) {
|
|
388
381
|
this.setSearch()
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="app-select-list-dialog full-width">
|
|
3
|
+
<header class="flex items-center justify-between no-wrap">
|
|
4
|
+
<qas-label v-bind="labelProps" />
|
|
5
|
+
|
|
6
|
+
<qas-btn
|
|
7
|
+
v-bind="defaultAddButtonProps"
|
|
8
|
+
@click="toggleDialog"
|
|
9
|
+
/>
|
|
10
|
+
</header>
|
|
11
|
+
|
|
12
|
+
<div
|
|
13
|
+
v-if="props.description"
|
|
14
|
+
class="q-mt-md text-body1 text-grey-8"
|
|
15
|
+
>
|
|
16
|
+
{{ props.description }}
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<qas-box
|
|
20
|
+
v-if="hasBox"
|
|
21
|
+
class="q-mt-md relative-position"
|
|
22
|
+
>
|
|
23
|
+
<span class="text-grey-9 text-subtitle1">
|
|
24
|
+
{{ props.listLabel }}
|
|
25
|
+
</span>
|
|
26
|
+
|
|
27
|
+
<q-virtual-scroll #default="{ item, index }" class="app-select-list-dialog__list q-mt-md" :items="selectedOptions" separator>
|
|
28
|
+
<q-item class="q-px-none text-body1 text-grey-8">
|
|
29
|
+
<q-item-section>
|
|
30
|
+
{{ item.label }}
|
|
31
|
+
</q-item-section>
|
|
32
|
+
|
|
33
|
+
<q-item-section avatar>
|
|
34
|
+
<qas-btn v-bind="getRemoveButtonProps({ index, option: item })" />
|
|
35
|
+
</q-item-section>
|
|
36
|
+
</q-item>
|
|
37
|
+
</q-virtual-scroll>
|
|
38
|
+
|
|
39
|
+
<q-inner-loading :showing="props.loading">
|
|
40
|
+
<q-spinner
|
|
41
|
+
color="grey"
|
|
42
|
+
size="2em"
|
|
43
|
+
/>
|
|
44
|
+
</q-inner-loading>
|
|
45
|
+
</qas-box>
|
|
46
|
+
|
|
47
|
+
<span
|
|
48
|
+
v-if="hasError"
|
|
49
|
+
class="app-select-list-dialog__error"
|
|
50
|
+
>
|
|
51
|
+
{{ errorMessage }}
|
|
52
|
+
</span>
|
|
53
|
+
|
|
54
|
+
<qas-dialog
|
|
55
|
+
v-bind="defaultDialogProps"
|
|
56
|
+
v-model="showDialog"
|
|
57
|
+
>
|
|
58
|
+
<template v-for="(_, name) in $slots" #[name]="context">
|
|
59
|
+
<slot :name="`dialog-${name}`" v-bind="context || {}" />
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<template #description>
|
|
63
|
+
<slot name="dialog-description">
|
|
64
|
+
<div v-if="dialogDescription" class="q-mb-xl text-center">
|
|
65
|
+
{{ dialogDescription }}
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<qas-select-list
|
|
69
|
+
v-model="listModel"
|
|
70
|
+
v-bind="defaultSelectListProps"
|
|
71
|
+
/>
|
|
72
|
+
</slot>
|
|
73
|
+
</template>
|
|
74
|
+
</qas-dialog>
|
|
75
|
+
</div>
|
|
76
|
+
</template>
|
|
77
|
+
|
|
78
|
+
<script setup>
|
|
79
|
+
import { computed, ref, watch } from 'vue'
|
|
80
|
+
|
|
81
|
+
defineOptions({ name: 'QasSelectListDialog' })
|
|
82
|
+
|
|
83
|
+
const props = defineProps({
|
|
84
|
+
addButtonProps: {
|
|
85
|
+
type: Object,
|
|
86
|
+
default: () => ({})
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
description: {
|
|
90
|
+
type: String,
|
|
91
|
+
default: ''
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
disable: {
|
|
95
|
+
type: Boolean
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
error: {
|
|
99
|
+
type: [String, Array],
|
|
100
|
+
default: ''
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
dialogProps: {
|
|
104
|
+
type: Object,
|
|
105
|
+
default: () => ({})
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
options: {
|
|
109
|
+
type: Array,
|
|
110
|
+
default: () => []
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
label: {
|
|
114
|
+
type: String,
|
|
115
|
+
default: ''
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
listLabel: {
|
|
119
|
+
type: String,
|
|
120
|
+
default: ''
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
loading: {
|
|
124
|
+
type: Boolean
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
modelValue: {
|
|
128
|
+
type: Array,
|
|
129
|
+
default: () => []
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
selectListProps: {
|
|
133
|
+
type: Object,
|
|
134
|
+
default: () => ({})
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
useLazyLoading: {
|
|
138
|
+
type: Boolean
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
const emit = defineEmits(['add', 'remove', 'update:modelValue'])
|
|
143
|
+
|
|
144
|
+
const hasError = computed(() => Array.isArray(props.error) ? !!props.error.length : !!props.error)
|
|
145
|
+
const errorMessage = computed(() => Array.isArray(props.error) ? props.error.join(' ') : props.error)
|
|
146
|
+
|
|
147
|
+
const {
|
|
148
|
+
listModel,
|
|
149
|
+
showDialog,
|
|
150
|
+
|
|
151
|
+
defaultDialogProps,
|
|
152
|
+
defaultSelectListProps,
|
|
153
|
+
dialogDescription,
|
|
154
|
+
|
|
155
|
+
toggleDialog
|
|
156
|
+
} = useSelectDialog()
|
|
157
|
+
|
|
158
|
+
const {
|
|
159
|
+
selectedOptions,
|
|
160
|
+
|
|
161
|
+
hasBox,
|
|
162
|
+
|
|
163
|
+
add,
|
|
164
|
+
removeAll,
|
|
165
|
+
remove,
|
|
166
|
+
getRemoveButtonProps
|
|
167
|
+
} = useList()
|
|
168
|
+
|
|
169
|
+
defineExpose({ add, removeAll, remove })
|
|
170
|
+
|
|
171
|
+
const model = ref([...props.modelValue])
|
|
172
|
+
|
|
173
|
+
const defaultAddButtonProps = computed(() => {
|
|
174
|
+
return {
|
|
175
|
+
icon: 'sym_r_add',
|
|
176
|
+
useLabelOnSmallScreen: false,
|
|
177
|
+
...props.addButtonProps,
|
|
178
|
+
disable: props.disable,
|
|
179
|
+
loading: props.loading
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const labelProps = computed(() => {
|
|
184
|
+
return {
|
|
185
|
+
label: props.label,
|
|
186
|
+
margin: 'none',
|
|
187
|
+
color: hasError.value ? 'negative' : 'grey-9'
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
const hasLazyLoading = computed(() => {
|
|
192
|
+
return props.useLazyLoading || !!props.selectListProps?.searchBoxProps?.useLazyLoading
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
watch(() => props.modelValue, newValue => {
|
|
196
|
+
model.value = [...newValue]
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
function updateModel () {
|
|
200
|
+
emit('update:modelValue', model.value)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ------------------------- composable functions ------------------------------
|
|
204
|
+
function useList () {
|
|
205
|
+
const filteredOptions = ref(props.options)
|
|
206
|
+
|
|
207
|
+
const selectedOptions = computed(() => {
|
|
208
|
+
const options = []
|
|
209
|
+
|
|
210
|
+
model.value.forEach(value => {
|
|
211
|
+
const option = filteredOptions.value.find(option => option.value === value)
|
|
212
|
+
|
|
213
|
+
if (option) return options.push(option)
|
|
214
|
+
|
|
215
|
+
options.push({ label: value })
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
return options
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
watch(() => props.options, options => {
|
|
222
|
+
filteredOptions.value = [...options]
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
const hasBox = computed(() => hasFilteredOptions.value || props.loading)
|
|
226
|
+
const hasFilteredOptions = computed(() => model.value.length)
|
|
227
|
+
|
|
228
|
+
/*
|
|
229
|
+
* caso não passe o value, o valor sera automaticamente o value da option
|
|
230
|
+
*/
|
|
231
|
+
function add ({ options = [], value }) {
|
|
232
|
+
const normalizedItems = Array.isArray(options) ? options : [options]
|
|
233
|
+
|
|
234
|
+
/*
|
|
235
|
+
* Validação necessária pois se não estiver com lazyloading e adicionar mais opções, as options vai vir duplicadas
|
|
236
|
+
* com as opções que já foram adicionadas
|
|
237
|
+
*/
|
|
238
|
+
if (hasLazyLoading.value) {
|
|
239
|
+
filteredOptions.value.push(...normalizedItems)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const newModel = value || normalizedItems.map(item => item.value)
|
|
243
|
+
|
|
244
|
+
model.value.push(...newModel)
|
|
245
|
+
|
|
246
|
+
emit('add', newModel)
|
|
247
|
+
|
|
248
|
+
updateModel()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function removeAll () {
|
|
252
|
+
model.value = []
|
|
253
|
+
filteredOptions.value = []
|
|
254
|
+
|
|
255
|
+
updateModel()
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function remove (value) {
|
|
259
|
+
const index = model.value.findIndex(item => item === value)
|
|
260
|
+
const optionIndex = filteredOptions.value.findIndex(option => option.value === value)
|
|
261
|
+
|
|
262
|
+
if (~index) model.value.splice(index, 1)
|
|
263
|
+
if (~optionIndex) filteredOptions.value.splice(optionIndex, 1)
|
|
264
|
+
|
|
265
|
+
emit('remove', value)
|
|
266
|
+
updateModel()
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function getRemoveButtonProps ({ option }) {
|
|
270
|
+
return {
|
|
271
|
+
color: 'grey-9',
|
|
272
|
+
icon: 'sym_r_delete',
|
|
273
|
+
variant: 'tertiary',
|
|
274
|
+
disable: props.disable || !!option.disable,
|
|
275
|
+
onClick: () => remove(option.value)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
filteredOptions,
|
|
281
|
+
selectedOptions,
|
|
282
|
+
|
|
283
|
+
hasBox,
|
|
284
|
+
hasFilteredOptions,
|
|
285
|
+
|
|
286
|
+
add,
|
|
287
|
+
removeAll,
|
|
288
|
+
remove,
|
|
289
|
+
getRemoveButtonProps
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function useSelectDialog () {
|
|
294
|
+
const showDialog = ref(false)
|
|
295
|
+
const listModel = ref([])
|
|
296
|
+
|
|
297
|
+
const defaultDialogProps = computed(() => {
|
|
298
|
+
return {
|
|
299
|
+
useFullMaxWidth: true,
|
|
300
|
+
|
|
301
|
+
...props.dialogProps,
|
|
302
|
+
|
|
303
|
+
onBeforeShow: event => {
|
|
304
|
+
resetListModel()
|
|
305
|
+
|
|
306
|
+
props.dialogProps.onBeforeShow && props.dialogProps.onBeforeShow(event)
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
ok: {
|
|
310
|
+
label: 'Adicionar',
|
|
311
|
+
|
|
312
|
+
disable: !listModel.value.length,
|
|
313
|
+
|
|
314
|
+
...props.dialogProps.ok,
|
|
315
|
+
|
|
316
|
+
onClick: () => {
|
|
317
|
+
props.dialogProps.ok?.onClick?.()
|
|
318
|
+
onAdd()
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
const defaultSelectListProps = computed(() => {
|
|
325
|
+
return {
|
|
326
|
+
emitValue: false,
|
|
327
|
+
|
|
328
|
+
...props.selectListProps,
|
|
329
|
+
|
|
330
|
+
searchBoxProps: {
|
|
331
|
+
useLazyLoading: props.useLazyLoading,
|
|
332
|
+
list: props.options,
|
|
333
|
+
height: '160px',
|
|
334
|
+
optionsToExclude: model.value,
|
|
335
|
+
|
|
336
|
+
...props.selectListProps.searchBoxProps
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
const dialogDescription = computed(() => props.dialogProps.card?.description)
|
|
342
|
+
|
|
343
|
+
function toggleDialog () {
|
|
344
|
+
showDialog.value = !showDialog.value
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function resetListModel () {
|
|
348
|
+
listModel.value = []
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function onAdd () {
|
|
352
|
+
if (listModel.value.length) add({ options: listModel.value })
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
listModel,
|
|
357
|
+
showDialog,
|
|
358
|
+
|
|
359
|
+
defaultDialogProps,
|
|
360
|
+
defaultSelectListProps,
|
|
361
|
+
dialogDescription,
|
|
362
|
+
|
|
363
|
+
toggleDialog
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
</script>
|
|
367
|
+
|
|
368
|
+
<style lang="scss">
|
|
369
|
+
.app-select-list-dialog {
|
|
370
|
+
.q-item {
|
|
371
|
+
@include set-typography($body1)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// simulando q-field--error
|
|
375
|
+
&__error {
|
|
376
|
+
padding-top: var(--qas-spacing-sm);
|
|
377
|
+
color: var(--q-negative) !important;
|
|
378
|
+
font-size: 12px;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
&__list {
|
|
382
|
+
max-height: 250px;
|
|
383
|
+
overflow-y: auto;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
</style>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
type: component
|
|
2
|
+
|
|
3
|
+
meta:
|
|
4
|
+
desc: Componente de seleção de itens dentro do QasDialog.
|
|
5
|
+
|
|
6
|
+
props:
|
|
7
|
+
add-button-props:
|
|
8
|
+
desc: Props repassadas para o botão de adicionar (QasBtn)
|
|
9
|
+
default: {}
|
|
10
|
+
type: Object
|
|
11
|
+
|
|
12
|
+
description:
|
|
13
|
+
desc: Descrição acima da lista de itens selecionados.
|
|
14
|
+
default: ''
|
|
15
|
+
type: String
|
|
16
|
+
|
|
17
|
+
disable:
|
|
18
|
+
desc: Desabilita o componente (remoção e adição).
|
|
19
|
+
default: false
|
|
20
|
+
type: Boolean
|
|
21
|
+
|
|
22
|
+
dialog-props:
|
|
23
|
+
desc: Propriedades repassadas para o dialog (QasDialog).
|
|
24
|
+
default: {}
|
|
25
|
+
type: Object
|
|
26
|
+
|
|
27
|
+
error:
|
|
28
|
+
desc: Mensagem de erro.
|
|
29
|
+
default: ''
|
|
30
|
+
type: String
|
|
31
|
+
|
|
32
|
+
options:
|
|
33
|
+
desc: Opções de itens disponíveis para seleção.
|
|
34
|
+
default: []
|
|
35
|
+
type: Array
|
|
36
|
+
|
|
37
|
+
label:
|
|
38
|
+
desc: Label do componente.
|
|
39
|
+
default: ''
|
|
40
|
+
type: String
|
|
41
|
+
|
|
42
|
+
list-label:
|
|
43
|
+
desc: Label da acima dos itens selecionados.
|
|
44
|
+
default: ''
|
|
45
|
+
type: String
|
|
46
|
+
|
|
47
|
+
loading:
|
|
48
|
+
desc: Estado de carregamento do componente.
|
|
49
|
+
default: false
|
|
50
|
+
type: Boolean
|
|
51
|
+
|
|
52
|
+
model-value:
|
|
53
|
+
desc: Model do componente.
|
|
54
|
+
default: []
|
|
55
|
+
type: Array
|
|
56
|
+
model: true
|
|
57
|
+
|
|
58
|
+
select-list-props:
|
|
59
|
+
desc: Propriedades repassadas para o componente QasSelectList.
|
|
60
|
+
default: []
|
|
61
|
+
type: Array
|
|
62
|
+
|
|
63
|
+
use-lazy-loading:
|
|
64
|
+
desc: O componente precisa saber se o lazy-loading esta habilitado para este componente, e caso esteja usando customização pelos slots do "dialog-description", é necessário informar por esta prop.
|
|
65
|
+
default: false
|
|
66
|
+
type: Boolean
|
|
67
|
+
|
|
68
|
+
slots:
|
|
69
|
+
dialog-actions:
|
|
70
|
+
desc: Slot para substituir ações do dialog.
|
|
71
|
+
|
|
72
|
+
dialog-description:
|
|
73
|
+
desc: Slot para substituir a descrição do dialog.
|
|
74
|
+
|
|
75
|
+
dialog-header:
|
|
76
|
+
desc: Slot para substituir cabeçalho do dialog.
|
|
77
|
+
|
|
78
|
+
events:
|
|
79
|
+
'add -> function(list)':
|
|
80
|
+
desc: Dispara toda vez que é adicionado novos itens.
|
|
81
|
+
params:
|
|
82
|
+
list:
|
|
83
|
+
desc: Lista de itens adicionados.
|
|
84
|
+
type: Array
|
|
85
|
+
|
|
86
|
+
'remove -> function(value)':
|
|
87
|
+
desc: Dispara toda vez que é removido um item.
|
|
88
|
+
params:
|
|
89
|
+
value:
|
|
90
|
+
desc: Valor do item (uuid/id/slug).
|
|
91
|
+
type: String
|
|
92
|
+
|
|
93
|
+
'update:model-value -> function(list)':
|
|
94
|
+
desc: Dispara toda vez que o model é atualizado.
|
|
95
|
+
params:
|
|
96
|
+
list:
|
|
97
|
+
desc: Lista de itens selecionados.
|
|
98
|
+
type: Array
|
|
99
|
+
|
|
100
|
+
methods:
|
|
101
|
+
'add ({ options = [], value }) => undefined':
|
|
102
|
+
desc: Adiciona itens a lista de selecionados, é preciso adicionar quais opções são referentes ao model.
|
|
103
|
+
|
|
104
|
+
'remove (value) => undefined':
|
|
105
|
+
desc: remove um item da lista de selecionados passando o valor referente a ele na lista.
|
|
106
|
+
|
|
107
|
+
'removeAll () => undefined':
|
|
108
|
+
desc: remove todos os itens da lista de selecionados.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<q-timeline
|
|
3
|
+
class="qas-timeline"
|
|
4
|
+
color="grey-6"
|
|
5
|
+
layout="comfortable"
|
|
6
|
+
>
|
|
7
|
+
<template
|
|
8
|
+
v-for="(item, index) in list"
|
|
9
|
+
:key="`timeline-${index}-${uid()}`"
|
|
10
|
+
>
|
|
11
|
+
<q-timeline-entry
|
|
12
|
+
side="left"
|
|
13
|
+
:subtitle="getFormattedValue(item, Masks.Date)"
|
|
14
|
+
>
|
|
15
|
+
<slot :item="item">
|
|
16
|
+
<div class="column justify-center q-gutter-sm q-py-md qas-timeline__content">
|
|
17
|
+
<slot :item="item" name="hour">
|
|
18
|
+
<div class="text-body2 text-grey-8">
|
|
19
|
+
Adicionado às {{ getFormattedValue(item, Masks.Hour) }}
|
|
20
|
+
</div>
|
|
21
|
+
</slot>
|
|
22
|
+
|
|
23
|
+
<slot :item="item" name="description">
|
|
24
|
+
<div class="text-body1 text-grey-9">
|
|
25
|
+
{{ item[descriptionKey] }}
|
|
26
|
+
</div>
|
|
27
|
+
</slot>
|
|
28
|
+
</div>
|
|
29
|
+
</slot>
|
|
30
|
+
</q-timeline-entry>
|
|
31
|
+
</template>
|
|
32
|
+
</q-timeline>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup>
|
|
36
|
+
import { date as dateFn } from '../../helpers/filters'
|
|
37
|
+
import { uid } from 'quasar'
|
|
38
|
+
|
|
39
|
+
defineOptions({ name: 'QasTimeline' })
|
|
40
|
+
|
|
41
|
+
const Masks = {
|
|
42
|
+
Hour: 'HH:mm',
|
|
43
|
+
Date: 'dd MMM yyyy'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const props = defineProps({
|
|
47
|
+
list: {
|
|
48
|
+
type: Array,
|
|
49
|
+
default: () => []
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
dateKey: {
|
|
53
|
+
type: String,
|
|
54
|
+
default: 'date'
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
hourKey: {
|
|
58
|
+
type: String,
|
|
59
|
+
default: 'date'
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
descriptionKey: {
|
|
63
|
+
type: String,
|
|
64
|
+
default: 'description'
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
function isInvalidDate (date) {
|
|
69
|
+
const day = new Date(date).getDay()
|
|
70
|
+
|
|
71
|
+
return isNaN(day)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getFormattedValue (item, mask) {
|
|
75
|
+
const itemKey = mask === Masks.Hour ? props.hourKey : props.dateKey
|
|
76
|
+
|
|
77
|
+
const date = item[itemKey]
|
|
78
|
+
|
|
79
|
+
return isInvalidDate(date) ? date : dateFn(date, mask)
|
|
80
|
+
}
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<style lang="scss">
|
|
84
|
+
.qas-timeline {
|
|
85
|
+
margin: 0;
|
|
86
|
+
|
|
87
|
+
&__content {
|
|
88
|
+
min-height: 100px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.q-timeline__subtitle {
|
|
92
|
+
color: $dark;
|
|
93
|
+
opacity: initial;
|
|
94
|
+
padding-right: 0;
|
|
95
|
+
position: relative;
|
|
96
|
+
text-align: center;
|
|
97
|
+
vertical-align: middle;
|
|
98
|
+
width: 80px;
|
|
99
|
+
|
|
100
|
+
&::before {
|
|
101
|
+
background-color: currentColor;
|
|
102
|
+
bottom: 0;
|
|
103
|
+
content: "";
|
|
104
|
+
display: block;
|
|
105
|
+
opacity: 0.4;
|
|
106
|
+
position: absolute;
|
|
107
|
+
right: -30px;
|
|
108
|
+
top: 0px;
|
|
109
|
+
transition: opacity var(--qas-generic-transition) ease;
|
|
110
|
+
width: 3px;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.q-timeline__entry {
|
|
115
|
+
&:hover,
|
|
116
|
+
.active {
|
|
117
|
+
.q-timeline__subtitle {
|
|
118
|
+
&::before {
|
|
119
|
+
background-color: $primary;
|
|
120
|
+
opacity: 1;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.q-timeline__content {
|
|
127
|
+
padding-bottom: 0;
|
|
128
|
+
vertical-align: middle;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.q-timeline__title {
|
|
132
|
+
margin-bottom: 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.q-timeline__dot::before,
|
|
136
|
+
.q-timeline__dot::after {
|
|
137
|
+
display: none;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
</style>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
type: component
|
|
2
|
+
|
|
3
|
+
meta:
|
|
4
|
+
desc: Componente para timeline que implementa o "QTimeline".
|
|
5
|
+
|
|
6
|
+
props:
|
|
7
|
+
list:
|
|
8
|
+
desc: Lista de itens com datas e descrições que serão exibidios na timeline.
|
|
9
|
+
default: []
|
|
10
|
+
type: Array
|
|
11
|
+
examples: ["[{ date: '2023-01-11T14:58:40.000000Z', description: 'Descrição' }"]
|
|
12
|
+
|
|
13
|
+
dateKey:
|
|
14
|
+
desc: Chave do campo que será exibido à esquerda da timeline.
|
|
15
|
+
default: date
|
|
16
|
+
type: String
|
|
17
|
+
|
|
18
|
+
hourKey:
|
|
19
|
+
desc: Chave do campo que será exibido acima da descrição.
|
|
20
|
+
default: date
|
|
21
|
+
type: String
|
|
22
|
+
|
|
23
|
+
descriptionKey:
|
|
24
|
+
desc: Chave do campo que será exibido na descrição.
|
|
25
|
+
default: description
|
|
26
|
+
type: String
|
|
27
|
+
|
|
28
|
+
slots:
|
|
29
|
+
default:
|
|
30
|
+
desc: slot para substituir o conteúdo todo da timeline.
|
|
31
|
+
|
|
32
|
+
hour:
|
|
33
|
+
desc: slot para substituir o conteúdo acima da descrição.
|
|
34
|
+
|
|
35
|
+
description:
|
|
36
|
+
desc: slot para substituir o conteúdo da descrição.
|
package/src/composables/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export { default as
|
|
1
|
+
export { default as useContext } from './use-context.js'
|
|
2
2
|
export { default as useForm } from './use-form.js'
|
|
3
|
+
export { default as useHistory } from './use-history.js'
|
|
4
|
+
export { default as useQueryCache } from './use-query-cache.js'
|
|
3
5
|
export { default as useScreen } from './use-screen.js'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { computed } from 'vue'
|
|
2
|
+
import { useRoute } from 'vue-router'
|
|
3
|
+
|
|
4
|
+
export default function () {
|
|
5
|
+
const route = useRoute()
|
|
6
|
+
|
|
7
|
+
const context = computed(() => {
|
|
8
|
+
const { limit, ordering, page, search, ...filters } = route.query
|
|
9
|
+
return { filters, limit, ordering, page: page ? parseInt(page) : 1, search }
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
context
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { SessionStorage } from 'quasar'
|
|
2
|
+
import { filterObject } from '../helpers'
|
|
3
|
+
|
|
4
|
+
const cachedFilters = SessionStorage.getItem('cachedFilters') || {}
|
|
5
|
+
|
|
6
|
+
function updateSessionStorage () {
|
|
7
|
+
SessionStorage.set('cachedFilters', cachedFilters)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function () {
|
|
11
|
+
function addOne (key, { label, value }) {
|
|
12
|
+
cachedFilters[key][label] = value
|
|
13
|
+
updateSessionStorage()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function addMany (key, filters) {
|
|
17
|
+
cachedFilters[key] = filters
|
|
18
|
+
updateSessionStorage()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function findOne (key, filter) {
|
|
22
|
+
return cachedFilters[key][filter]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function findAll (key) {
|
|
26
|
+
return cachedFilters[key]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function clearOne (key, filter) {
|
|
30
|
+
delete cachedFilters[key][filter]
|
|
31
|
+
updateSessionStorage()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function clearAll (key, { exclude = [] } = {}) {
|
|
35
|
+
if (exclude.length) {
|
|
36
|
+
cachedFilters[key] = filterObject(cachedFilters[key], exclude)
|
|
37
|
+
} else {
|
|
38
|
+
delete cachedFilters[key]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
updateSessionStorage()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
addOne,
|
|
46
|
+
addMany,
|
|
47
|
+
findOne,
|
|
48
|
+
findAll,
|
|
49
|
+
clearOne,
|
|
50
|
+
clearAll,
|
|
51
|
+
cachedFilters
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/vue-plugin.js
CHANGED
|
@@ -21,7 +21,6 @@ import QasDialog from './components/dialog/QasDialog.vue'
|
|
|
21
21
|
import QasDialogRouter from './components/dialog-router/QasDialogRouter.vue'
|
|
22
22
|
import QasEmptyResultText from './components/empty-result-text/QasEmptyResultText.vue'
|
|
23
23
|
import QasField from './components/field/QasField.vue'
|
|
24
|
-
import QasSearchInput from './components/search-input/QasSearchInput.vue'
|
|
25
24
|
import QasFilters from './components/filters/QasFilters.vue'
|
|
26
25
|
import QasFormGenerator from './components/form-generator/QasFormGenerator.vue'
|
|
27
26
|
import QasFormView from './components/form-view/QasFormView.vue'
|
|
@@ -44,8 +43,10 @@ import QasPasswordStrengthChecker from './components/password-strength-checker/Q
|
|
|
44
43
|
import QasProfile from './components/profile/QasProfile.vue'
|
|
45
44
|
import QasResizer from './components/resizer/QasResizer.vue'
|
|
46
45
|
import QasSearchBox from './components/search-box/QasSearchBox.vue'
|
|
46
|
+
import QasSearchInput from './components/search-input/QasSearchInput.vue'
|
|
47
47
|
import QasSelect from './components/select/QasSelect.vue'
|
|
48
48
|
import QasSelectList from './components/select-list/QasSelectList.vue'
|
|
49
|
+
import QasSelectListDialog from './components/select-list-dialog/QasSelectListDialog.vue'
|
|
49
50
|
import QasSignaturePad from './components/signature-pad/QasSignaturePad.vue'
|
|
50
51
|
import QasSignatureUploader from './components/signature-uploader/QasSignatureUploader.vue'
|
|
51
52
|
import QasSingleView from './components/single-view/QasSingleView.vue'
|
|
@@ -54,6 +55,7 @@ import QasStatus from './components/status/QasStatus.vue'
|
|
|
54
55
|
import QasTableGenerator from './components/table-generator/QasTableGenerator.vue'
|
|
55
56
|
import QasTabsGenerator from './components/tabs-generator/QasTabsGenerator.vue'
|
|
56
57
|
import QasTextTruncate from './components/text-truncate/QasTextTruncate.vue'
|
|
58
|
+
import QasTimeline from './components/timeline/QasTimeline.vue'
|
|
57
59
|
import QasTransfer from './components/transfer/QasTransfer.vue'
|
|
58
60
|
import QasTreeGenerator from './components/tree-generator/QasTreeGenerator.vue'
|
|
59
61
|
import QasUploader from './components/uploader/QasUploader.vue'
|
|
@@ -102,7 +104,6 @@ async function install (app) {
|
|
|
102
104
|
app.component('QasDialogRouter', QasDialogRouter)
|
|
103
105
|
app.component('QasEmptyResultText', QasEmptyResultText)
|
|
104
106
|
app.component('QasField', QasField)
|
|
105
|
-
app.component('QasSearchInput', QasSearchInput)
|
|
106
107
|
app.component('QasFilters', QasFilters)
|
|
107
108
|
app.component('QasFormGenerator', QasFormGenerator)
|
|
108
109
|
app.component('QasFormView', QasFormView)
|
|
@@ -125,8 +126,10 @@ async function install (app) {
|
|
|
125
126
|
app.component('QasProfile', QasProfile)
|
|
126
127
|
app.component('QasResizer', QasResizer)
|
|
127
128
|
app.component('QasSearchBox', QasSearchBox)
|
|
129
|
+
app.component('QasSearchInput', QasSearchInput)
|
|
128
130
|
app.component('QasSelect', QasSelect)
|
|
129
131
|
app.component('QasSelectList', QasSelectList)
|
|
132
|
+
app.component('QasSelectListDialog', QasSelectListDialog)
|
|
130
133
|
app.component('QasSignaturePad', QasSignaturePad)
|
|
131
134
|
app.component('QasSignatureUploader', QasSignatureUploader)
|
|
132
135
|
app.component('QasSingleView', QasSingleView)
|
|
@@ -135,6 +138,7 @@ async function install (app) {
|
|
|
135
138
|
app.component('QasTableGenerator', QasTableGenerator)
|
|
136
139
|
app.component('QasTabsGenerator', QasTabsGenerator)
|
|
137
140
|
app.component('QasTextTruncate', QasTextTruncate)
|
|
141
|
+
app.component('QasTimeline', QasTimeline)
|
|
138
142
|
app.component('QasTransfer', QasTransfer)
|
|
139
143
|
app.component('QasTreeGenerator', QasTreeGenerator)
|
|
140
144
|
app.component('QasUploader', QasUploader)
|
|
@@ -181,7 +185,6 @@ export {
|
|
|
181
185
|
QasDialogRouter,
|
|
182
186
|
QasEmptyResultText,
|
|
183
187
|
QasField,
|
|
184
|
-
QasSearchInput,
|
|
185
188
|
QasFilters,
|
|
186
189
|
QasFormGenerator,
|
|
187
190
|
QasFormView,
|
|
@@ -204,8 +207,10 @@ export {
|
|
|
204
207
|
QasProfile,
|
|
205
208
|
QasResizer,
|
|
206
209
|
QasSearchBox,
|
|
210
|
+
QasSearchInput,
|
|
207
211
|
QasSelect,
|
|
208
212
|
QasSelectList,
|
|
213
|
+
QasSelectListDialog,
|
|
209
214
|
QasSignaturePad,
|
|
210
215
|
QasSignatureUploader,
|
|
211
216
|
QasSingleView,
|
|
@@ -214,6 +219,7 @@ export {
|
|
|
214
219
|
QasTableGenerator,
|
|
215
220
|
QasTabsGenerator,
|
|
216
221
|
QasTextTruncate,
|
|
222
|
+
QasTimeline,
|
|
217
223
|
QasTransfer,
|
|
218
224
|
QasTreeGenerator,
|
|
219
225
|
QasUploader,
|