@bildvitta/quasar-ui-asteroid 3.17.0-beta.8 → 3.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/assets/sounds/nave-notification.mp3 +0 -0
- package/src/components/app-menu/QasAppMenu.vue +73 -15
- package/src/components/app-menu/QasAppMenu.yml +5 -5
- package/src/components/app-user/QasAppUser.vue +49 -40
- package/src/components/avatar/QasAvatar.vue +7 -8
- package/src/components/badge/QasBadge.vue +3 -29
- package/src/components/board-generator/QasBoardGenerator.vue +442 -40
- package/src/components/board-generator/QasBoardGenerator.yml +107 -12
- package/src/components/card/QasCard.vue +13 -4
- package/src/components/chart-view/QasChartView.vue +56 -3
- package/src/components/chart-view/QasChartView.yml +6 -0
- package/src/components/checkbox/QasCheckbox.vue +67 -11
- package/src/components/checkbox/QasCheckbox.yml +18 -0
- package/src/components/copy/QasCopy.vue +12 -2
- package/src/components/copy/QasCopy.yml +8 -0
- package/src/components/expansion-item/QasExpansionItem.vue +108 -76
- package/src/components/expansion-item/QasExpansionItem.yml +38 -10
- package/src/components/field/QasField.vue +1 -1
- package/src/components/form-generator/QasFormGenerator.vue +23 -10
- package/src/components/form-generator/QasFormGenerator.yml +2 -2
- package/src/components/grabbable/QasGrabbable.vue +14 -6
- package/src/components/grabbable/QasGrabbable.yml +4 -0
- package/src/components/grid-generator/QasGridGenerator.vue +3 -3
- package/src/components/grid-generator/QasGridGenerator.yml +2 -2
- package/src/components/grid-item/QasGridItem.vue +1 -1
- package/src/components/header/QasHeader.vue +11 -9
- package/src/components/infinite-scroll/QasInfiniteScroll.vue +16 -17
- package/src/components/infinite-scroll/QasInfiniteScroll.yml +7 -0
- package/src/components/list-view/QasListView.vue +16 -2
- package/src/components/list-view/QasListView.yml +9 -0
- package/src/components/radio/QasRadio.vue +24 -5
- package/src/components/radio/QasRadio.yml +6 -0
- package/src/components/select/QasSelect.vue +54 -7
- package/src/components/select/QasSelect.yml +6 -1
- package/src/components/select-filter/QasSelectFilter.vue +65 -0
- package/src/components/select-filter/QasSelectFilter.yml +36 -0
- package/src/components/stepper/QasStepper.vue +50 -3
- package/src/components/stepper-form-view/QasStepperFormView.vue +6 -4
- package/src/components/stepper-form-view/QasStepperFormView.yml +1 -1
- package/src/components/table-generator/QasTableGenerator.vue +3 -0
- package/src/components/text-truncate/QasTextTruncate.vue +77 -14
- package/src/components/text-truncate/QasTextTruncate.yml +14 -3
- package/src/components/uploader/QasUploader.vue +70 -24
- package/src/components/uploader/QasUploader.yml +15 -1
- package/src/components/welcome/QasWelcome.vue +8 -0
- package/src/components/welcome/QasWelcome.yml +3 -0
- package/src/composables/index.js +3 -1
- package/src/composables/private/index.js +1 -0
- package/src/composables/private/use-auth-user.js +20 -0
- package/src/composables/private/use-generator.js +20 -5
- package/src/composables/use-default-filters.js +106 -0
- package/src/composables/use-notifications.js +14 -0
- package/src/composables/use-query-cache.js +1 -1
- package/src/css/components/field.scss +13 -6
- package/src/helpers/set-scroll-on-grab.js +9 -1
- package/src/shared/badge-config.js +29 -0
- package/src/vue-plugin.js +3 -0
- package/src/components/app-menu/private/PvAppMenuHelpChat.vue +0 -222
|
@@ -2,7 +2,17 @@
|
|
|
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>
|
|
6
|
+
<div v-if="hasBadges" class="items-center q-col-gutter-sm row" :class="badgeParentClasses">
|
|
7
|
+
<div v-for="(item, index) in normalizedBadgesList" :key="index">
|
|
8
|
+
<qas-badge v-bind="getBadgeProps(item)" />
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div v-else class="ellipsis">
|
|
13
|
+
{{ formattedText }}
|
|
14
|
+
</div>
|
|
15
|
+
</slot>
|
|
6
16
|
</div>
|
|
7
17
|
|
|
8
18
|
<qas-btn v-if="hasButton" class="q-ml-sm" :label="buttonLabel" @click.stop.prevent="toggle" />
|
|
@@ -10,7 +20,7 @@
|
|
|
10
20
|
|
|
11
21
|
<qas-dialog v-model="show" v-bind="defaultProps" aria-label="Diálogo de texto completo" role="dialog">
|
|
12
22
|
<template v-if="isCounterMode" #description>
|
|
13
|
-
<div class="q-col-gutter-y-
|
|
23
|
+
<div class="q-col-gutter-y-sm row">
|
|
14
24
|
<div
|
|
15
25
|
v-for="(item, index) in normalizedList"
|
|
16
26
|
:key="index"
|
|
@@ -25,17 +35,19 @@
|
|
|
25
35
|
</template>
|
|
26
36
|
|
|
27
37
|
<script setup>
|
|
38
|
+
import QasDialog from '../dialog/QasDialog.vue'
|
|
39
|
+
|
|
40
|
+
import { baseProps } from '../../shared/badge-config'
|
|
41
|
+
|
|
28
42
|
import {
|
|
29
43
|
computed,
|
|
44
|
+
nextTick,
|
|
30
45
|
onMounted,
|
|
31
46
|
onUnmounted,
|
|
32
47
|
ref,
|
|
33
48
|
watch
|
|
34
49
|
} from 'vue'
|
|
35
50
|
|
|
36
|
-
import QasDialog from '../dialog/QasDialog.vue'
|
|
37
|
-
|
|
38
|
-
// define component name
|
|
39
51
|
defineOptions({ name: 'QasTextTruncate' })
|
|
40
52
|
|
|
41
53
|
// props
|
|
@@ -55,6 +67,11 @@ const props = defineProps({
|
|
|
55
67
|
default: ''
|
|
56
68
|
},
|
|
57
69
|
|
|
70
|
+
emptyText: {
|
|
71
|
+
type: String,
|
|
72
|
+
default: '-'
|
|
73
|
+
},
|
|
74
|
+
|
|
58
75
|
maxWidth: {
|
|
59
76
|
type: Number,
|
|
60
77
|
default: 0
|
|
@@ -85,13 +102,16 @@ const props = defineProps({
|
|
|
85
102
|
default: () => []
|
|
86
103
|
},
|
|
87
104
|
|
|
105
|
+
useBadge: {
|
|
106
|
+
type: Boolean
|
|
107
|
+
},
|
|
108
|
+
|
|
88
109
|
useObjectList: {
|
|
89
110
|
type: Boolean
|
|
90
111
|
},
|
|
91
112
|
|
|
92
|
-
|
|
93
|
-
type:
|
|
94
|
-
default: '-'
|
|
113
|
+
useWrapBadge: {
|
|
114
|
+
type: Boolean
|
|
95
115
|
}
|
|
96
116
|
})
|
|
97
117
|
|
|
@@ -100,11 +120,18 @@ const truncate = ref(null)
|
|
|
100
120
|
const parent = ref(null)
|
|
101
121
|
|
|
102
122
|
// composable
|
|
123
|
+
const {
|
|
124
|
+
hasBadges,
|
|
125
|
+
badgeParentClasses,
|
|
126
|
+
normalizedBadgesList,
|
|
127
|
+
getBadgeProps
|
|
128
|
+
} = useBadgeHandler()
|
|
129
|
+
|
|
103
130
|
const {
|
|
104
131
|
textContent,
|
|
105
132
|
isTruncated,
|
|
106
133
|
truncateText
|
|
107
|
-
} = useTruncate({ parent, props })
|
|
134
|
+
} = useTruncate({ parent, props, hasBadges })
|
|
108
135
|
|
|
109
136
|
const {
|
|
110
137
|
defaultProps,
|
|
@@ -122,6 +149,7 @@ const {
|
|
|
122
149
|
|
|
123
150
|
useMutationObserver({ truncate, callbackFn: truncateText })
|
|
124
151
|
|
|
152
|
+
// computeds
|
|
125
153
|
const classes = computed(() => [`text-${props.color}`, `text-${props.typography}`])
|
|
126
154
|
|
|
127
155
|
const formattedText = computed(() => props.list.length || props.text ? displayText.value : props.emptyText)
|
|
@@ -182,7 +210,7 @@ function useMutationObserver ({ truncate, callbackFn = () => {} }) {
|
|
|
182
210
|
}
|
|
183
211
|
}
|
|
184
212
|
|
|
185
|
-
function useTruncate ({ parent, props }) {
|
|
213
|
+
function useTruncate ({ parent, props, hasBadges }) {
|
|
186
214
|
// reactive vars
|
|
187
215
|
const maxPossibleWidth = ref('')
|
|
188
216
|
const textContent = ref('')
|
|
@@ -191,14 +219,19 @@ function useTruncate ({ parent, props }) {
|
|
|
191
219
|
// lifecycle
|
|
192
220
|
onMounted(() => truncateText())
|
|
193
221
|
|
|
194
|
-
// watch
|
|
195
|
-
watch(() => props.maxWidth, truncateText)
|
|
196
|
-
|
|
197
222
|
// computed
|
|
198
223
|
const isTruncated = computed(() => textWidth.value > maxPossibleWidth.value)
|
|
199
224
|
|
|
225
|
+
// watch
|
|
226
|
+
watch(() => props.maxWidth, truncateText)
|
|
227
|
+
|
|
200
228
|
// functions
|
|
201
|
-
function truncateText () {
|
|
229
|
+
async function truncateText () {
|
|
230
|
+
await nextTick()
|
|
231
|
+
|
|
232
|
+
// Se tiver badges, então não pode ser feito calculo de width.
|
|
233
|
+
if (hasBadges.value) return
|
|
234
|
+
|
|
202
235
|
parent.value.style.maxWidth = '100%'
|
|
203
236
|
textWidth.value = truncate.value.clientWidth
|
|
204
237
|
textContent.value = truncate.value?.innerHTML
|
|
@@ -271,4 +304,34 @@ function useCounter () {
|
|
|
271
304
|
counterLabel
|
|
272
305
|
}
|
|
273
306
|
}
|
|
307
|
+
|
|
308
|
+
function useBadgeHandler () {
|
|
309
|
+
const hasBadges = computed(() => props.useBadge && props.useObjectList && props.list.length)
|
|
310
|
+
|
|
311
|
+
const normalizedBadgesList = computed(() => props.list.slice(0, props.maxVisibleItem))
|
|
312
|
+
const badgeParentClasses = computed(() => ({ 'no-wrap': !props.useWrapBadge }))
|
|
313
|
+
|
|
314
|
+
function getBadgeProps (item) {
|
|
315
|
+
const itemProps = {}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* recupera somente keys que estão em baseProps do QasBadge
|
|
319
|
+
* pra evitar que passe propriedades desnecessárias
|
|
320
|
+
*/
|
|
321
|
+
for (const key in item) {
|
|
322
|
+
if (baseProps[key]) {
|
|
323
|
+
itemProps[key] = item[key]
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return itemProps
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
hasBadges,
|
|
332
|
+
badgeParentClasses,
|
|
333
|
+
normalizedBadgesList,
|
|
334
|
+
getBadgeProps
|
|
335
|
+
}
|
|
336
|
+
}
|
|
274
337
|
</script>
|
|
@@ -18,6 +18,10 @@ props:
|
|
|
18
18
|
desc: Seta o título do dialog.
|
|
19
19
|
type: String
|
|
20
20
|
|
|
21
|
+
empty-text:
|
|
22
|
+
desc: Texto a ser exibido no caso de não houver itens no "list" ou o "text" for vazio.
|
|
23
|
+
type: String
|
|
24
|
+
|
|
21
25
|
max-width:
|
|
22
26
|
desc: Seta o tamanho máximo do texto.
|
|
23
27
|
type: Number
|
|
@@ -47,13 +51,20 @@ props:
|
|
|
47
51
|
default: []
|
|
48
52
|
type: Array
|
|
49
53
|
|
|
54
|
+
use-badge:
|
|
55
|
+
desc: Habilita badges para cada item da lista.
|
|
56
|
+
default: false
|
|
57
|
+
type: Boolean
|
|
58
|
+
|
|
50
59
|
use-object-list:
|
|
51
60
|
desc: Utiliza a propriedade "list" como array de objeto contendo label (uso junto a prop list).
|
|
61
|
+
default: false
|
|
52
62
|
type: Boolean
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
desc:
|
|
56
|
-
|
|
64
|
+
use-wrap-badge:
|
|
65
|
+
desc: habilita a quebra de linha das badges quando o texto for muito grande.
|
|
66
|
+
default: false
|
|
67
|
+
type: Boolean
|
|
57
68
|
|
|
58
69
|
slots:
|
|
59
70
|
default:
|
|
@@ -3,18 +3,20 @@
|
|
|
3
3
|
<q-uploader ref="uploader" auto-upload class="bg-transparent" :class="uploaderClasses" v-bind="attributes" :factory="factory" flat :max-files="maxFiles" method="PUT" @factory-failed="factoryFailed" @uploaded="uploaded" @uploading="updateUploading(true)">
|
|
4
4
|
<template #header="scope">
|
|
5
5
|
<slot name="header" :scope="scope">
|
|
6
|
-
<
|
|
7
|
-
<
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
<div v-if="errorMessage" class="q-mt-xs text-caption text-negative">
|
|
11
|
-
{{ errorMessage }}
|
|
6
|
+
<qas-header class="q-mb-none" v-bind="getHeaderProps(scope)">
|
|
7
|
+
<template #description>
|
|
8
|
+
<div :class="headerDescriptionClasses">
|
|
9
|
+
{{ headerProps.description }}
|
|
12
10
|
</div>
|
|
13
|
-
</
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<template v-for="(_, name) in $slots" #[getHeaderSlotName(name)]="context">
|
|
14
|
+
<slot :name v-bind="context" />
|
|
15
|
+
</template>
|
|
16
|
+
</qas-header>
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
</div>
|
|
18
|
+
<div v-if="errorMessage" class="q-mt-xs text-caption text-negative">
|
|
19
|
+
{{ errorMessage }}
|
|
18
20
|
</div>
|
|
19
21
|
|
|
20
22
|
<!-- ------------------------------------ tags hidden -------------------------------------- -->
|
|
@@ -24,15 +26,13 @@
|
|
|
24
26
|
</template>
|
|
25
27
|
|
|
26
28
|
<template #list="scope">
|
|
27
|
-
<div v-if="hasGalleryCardSection(getFilesList(scope.files, scope))" class="q-col-gutter-lg
|
|
29
|
+
<div v-if="hasGalleryCardSection(getFilesList(scope.files, scope))" class="q-col-gutter-lg row">
|
|
28
30
|
<div v-for="(file, key, index) in getFilesList(scope.files, scope)" :key="index" :class="columnClasses">
|
|
29
31
|
<pv-uploader-gallery-card v-bind="getUploaderGalleryCardProps({ key, scope, file, index })" />
|
|
30
32
|
</div>
|
|
31
33
|
</div>
|
|
32
34
|
|
|
33
|
-
<
|
|
34
|
-
<qas-empty-result-text />
|
|
35
|
-
</div>
|
|
35
|
+
<qas-empty-result-text v-else />
|
|
36
36
|
</template>
|
|
37
37
|
</q-uploader>
|
|
38
38
|
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
|
|
43
43
|
<script>
|
|
44
44
|
import PvUploaderGalleryCard from './private/PvUploaderGalleryCard.vue'
|
|
45
|
+
import QasHeader from '../header/QasHeader.vue'
|
|
45
46
|
|
|
46
47
|
import { uid, extend } from 'quasar'
|
|
47
48
|
import { NotifyError } from '../../plugins'
|
|
@@ -53,7 +54,8 @@ export default {
|
|
|
53
54
|
name: 'QasUploader',
|
|
54
55
|
|
|
55
56
|
components: {
|
|
56
|
-
PvUploaderGalleryCard
|
|
57
|
+
PvUploaderGalleryCard,
|
|
58
|
+
QasHeader
|
|
57
59
|
},
|
|
58
60
|
|
|
59
61
|
inheritAttrs: false,
|
|
@@ -125,6 +127,11 @@ export default {
|
|
|
125
127
|
type: Object
|
|
126
128
|
},
|
|
127
129
|
|
|
130
|
+
headerProps: {
|
|
131
|
+
type: Object,
|
|
132
|
+
default: () => ({})
|
|
133
|
+
},
|
|
134
|
+
|
|
128
135
|
label: {
|
|
129
136
|
type: String,
|
|
130
137
|
default: ''
|
|
@@ -199,6 +206,12 @@ export default {
|
|
|
199
206
|
return this.$attrs
|
|
200
207
|
},
|
|
201
208
|
|
|
209
|
+
headerDescriptionClasses () {
|
|
210
|
+
return {
|
|
211
|
+
'text-negative': this.error
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
|
|
202
215
|
columnClasses () {
|
|
203
216
|
const irregularClasses = ['col']
|
|
204
217
|
const columns = this.defaultColumns
|
|
@@ -290,15 +303,6 @@ export default {
|
|
|
290
303
|
return this.$attrs.multiple || this.$attrs.multiple === ''
|
|
291
304
|
},
|
|
292
305
|
|
|
293
|
-
labelProps () {
|
|
294
|
-
return {
|
|
295
|
-
label: this.label,
|
|
296
|
-
margin: 'none',
|
|
297
|
-
|
|
298
|
-
...(this.error && { color: 'negative' })
|
|
299
|
-
}
|
|
300
|
-
},
|
|
301
|
-
|
|
302
306
|
self () {
|
|
303
307
|
return this
|
|
304
308
|
},
|
|
@@ -613,6 +617,48 @@ export default {
|
|
|
613
617
|
const emptyModel = this.isMultiple ? [] : this.useObjectModel ? {} : ''
|
|
614
618
|
|
|
615
619
|
this.$emit('update:modelValue', emptyModel)
|
|
620
|
+
},
|
|
621
|
+
|
|
622
|
+
getHeaderSlotName (name) {
|
|
623
|
+
return name.replace('header-', '')
|
|
624
|
+
},
|
|
625
|
+
|
|
626
|
+
getHeaderProps (scope) {
|
|
627
|
+
const { labelProps, actionsMenuProps, ...othersHeaderProps } = this.headerProps
|
|
628
|
+
|
|
629
|
+
const { list, ...othersActionsMenuProps } = actionsMenuProps || {}
|
|
630
|
+
|
|
631
|
+
return {
|
|
632
|
+
spacing: 'lg',
|
|
633
|
+
|
|
634
|
+
labelProps: {
|
|
635
|
+
label: this.label,
|
|
636
|
+
|
|
637
|
+
margin: 'none',
|
|
638
|
+
|
|
639
|
+
...labelProps,
|
|
640
|
+
|
|
641
|
+
...(this.error && { color: 'negative' })
|
|
642
|
+
},
|
|
643
|
+
|
|
644
|
+
...(this.hasAddFile && {
|
|
645
|
+
actionsMenuProps: {
|
|
646
|
+
list: {
|
|
647
|
+
add: {
|
|
648
|
+
icon: 'sym_r_add',
|
|
649
|
+
label: this.addButtonLabel,
|
|
650
|
+
handler: () => this.onAddButtonClick(scope)
|
|
651
|
+
},
|
|
652
|
+
|
|
653
|
+
...list
|
|
654
|
+
},
|
|
655
|
+
|
|
656
|
+
...othersActionsMenuProps
|
|
657
|
+
}
|
|
658
|
+
}),
|
|
659
|
+
|
|
660
|
+
...othersHeaderProps
|
|
661
|
+
}
|
|
616
662
|
}
|
|
617
663
|
}
|
|
618
664
|
}
|
|
@@ -83,8 +83,13 @@ props:
|
|
|
83
83
|
default: {}
|
|
84
84
|
type: Object
|
|
85
85
|
|
|
86
|
+
header-props:
|
|
87
|
+
desc: Propriedades repassadas para o componente "QasHeader".
|
|
88
|
+
default: {}
|
|
89
|
+
type: Object
|
|
90
|
+
|
|
86
91
|
label:
|
|
87
|
-
desc: Label do componente
|
|
92
|
+
desc: Label do componente.
|
|
88
93
|
type: String
|
|
89
94
|
|
|
90
95
|
max-files:
|
|
@@ -158,6 +163,15 @@ slots:
|
|
|
158
163
|
default: {}
|
|
159
164
|
type: Object
|
|
160
165
|
|
|
166
|
+
header-actions:
|
|
167
|
+
desc: Acessa o slot actions do "QasHeader".
|
|
168
|
+
|
|
169
|
+
header-description:
|
|
170
|
+
desc: Acessa o slot description do "QasHeader".
|
|
171
|
+
|
|
172
|
+
header-label:
|
|
173
|
+
desc: Acessa o slot label do "QasHeader".
|
|
174
|
+
|
|
161
175
|
list:
|
|
162
176
|
desc: Acesso ao conteúdo onde fica a listagem de arquivos.
|
|
163
177
|
scope:
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
</slot>
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
|
+
<div v-if="hasAfterGreetingSlot" class="q-mt-lg">
|
|
18
|
+
<slot name="after-greeting" />
|
|
19
|
+
</div>
|
|
20
|
+
|
|
17
21
|
<div v-if="hasShortcuts">
|
|
18
22
|
<qas-label class="q-mt-lg" label="Atalhos" />
|
|
19
23
|
|
|
@@ -102,6 +106,10 @@ export default {
|
|
|
102
106
|
if (time >= '12:00' && time < '18:59') return 'Boa tarde'
|
|
103
107
|
|
|
104
108
|
return 'Boa noite'
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
hasAfterGreetingSlot () {
|
|
112
|
+
return !!this.$slots['after-greeting']
|
|
105
113
|
}
|
|
106
114
|
}
|
|
107
115
|
}
|
package/src/composables/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export { default as useContext } from './use-context.js'
|
|
2
|
+
export { default as useDefaultFilters } from './use-default-filters.js'
|
|
2
3
|
export { default as useForm } from './use-form.js'
|
|
3
4
|
export { default as useHistory } from './use-history.js'
|
|
5
|
+
export { default as useNotifications } from './use-notifications.js'
|
|
4
6
|
export { default as useQueryCache } from './use-query-cache.js'
|
|
5
7
|
export { default as useScreen } from './use-screen.js'
|
|
6
|
-
export { default as useNotifications } from './use-notifications.js'
|
|
7
8
|
|
|
8
9
|
export * from './use-notifications.js'
|
|
10
|
+
export * from './use-default-filters.js'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { LocalStorage } from 'quasar'
|
|
2
|
+
|
|
3
|
+
import { computed, ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
const user = ref(LocalStorage.getItem('user'))
|
|
6
|
+
|
|
7
|
+
export default function useAuthUser () {
|
|
8
|
+
window.addEventListener('message', ({ data }) => {
|
|
9
|
+
if (data.type !== 'updateUser') return
|
|
10
|
+
|
|
11
|
+
user.value = data.user
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const hasUser = computed(() => !!user.value)
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
user,
|
|
18
|
+
hasUser
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { computed } from 'vue'
|
|
2
1
|
import { Spacing } from '../../enums/Spacing'
|
|
3
2
|
import { gutterValidator } from '../../helpers/private/gutter-validator'
|
|
3
|
+
import useScreen from '../use-screen'
|
|
4
|
+
|
|
5
|
+
import { computed } from 'vue'
|
|
4
6
|
|
|
5
7
|
const IRREGULAR_CLASSES = ['col', 'col-auto', 'fit']
|
|
6
8
|
|
|
@@ -21,7 +23,7 @@ export const baseProps = {
|
|
|
21
23
|
},
|
|
22
24
|
|
|
23
25
|
gutter: {
|
|
24
|
-
default:
|
|
26
|
+
default: undefined,
|
|
25
27
|
type: [String, Boolean],
|
|
26
28
|
validator: gutterValidator
|
|
27
29
|
}
|
|
@@ -34,20 +36,33 @@ export const baseProps = {
|
|
|
34
36
|
* @name useGenerator
|
|
35
37
|
* @param {Object} options - Opções do componente.
|
|
36
38
|
* @param {baseProps} options.props - Propriedades do componente.
|
|
39
|
+
* @param {boolean} options.isGrid - Propriedades do componente.
|
|
37
40
|
* @returns {{
|
|
38
41
|
* classes: classes,
|
|
39
42
|
* getFieldClass: getFieldClass
|
|
40
43
|
* }}
|
|
41
44
|
*/
|
|
42
|
-
export default function ({ props = {} }) {
|
|
45
|
+
export default function ({ props = {}, isGrid = false }) {
|
|
46
|
+
const screen = useScreen()
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Se a propriedade gutter não for passada, será calculada automaticamente.
|
|
50
|
+
* se for usado no grid e for inline, o gutter será menor.
|
|
51
|
+
*/
|
|
52
|
+
const defaultGutter = computed(() => {
|
|
53
|
+
if (props.gutter !== undefined) return props.gutter
|
|
54
|
+
|
|
55
|
+
return isGrid && (props.useInline && !screen.isSmall) ? Spacing.Sm : Spacing.Md
|
|
56
|
+
})
|
|
57
|
+
|
|
43
58
|
/**
|
|
44
59
|
* @type {{ value: string[] | string }}
|
|
45
60
|
*/
|
|
46
61
|
const classes = computed(() => {
|
|
47
62
|
const classesList = ['row']
|
|
48
63
|
|
|
49
|
-
if (
|
|
50
|
-
classesList.push(`q-col-gutter-${
|
|
64
|
+
if (defaultGutter.value) {
|
|
65
|
+
classesList.push(`q-col-gutter-${defaultGutter.value}`)
|
|
51
66
|
}
|
|
52
67
|
|
|
53
68
|
return classesList
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { computed, ref } from 'vue'
|
|
2
|
+
import { LocalStorage, is } from 'quasar'
|
|
3
|
+
|
|
4
|
+
const filterQuery = ref({})
|
|
5
|
+
|
|
6
|
+
const defaultFiltersHooks = {
|
|
7
|
+
defaultFiltersChange: []
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Define os filtros padrão antes de entrar na rota.
|
|
12
|
+
*
|
|
13
|
+
* Prioridade de aplicação dos filtros:
|
|
14
|
+
* 1 - Se o filtro estiver salvo no estado global, então utiliza ele.
|
|
15
|
+
* 2 - Se o filtro já estiver na query, então utiliza ele.
|
|
16
|
+
* 3 - Se o filtro já estiver salvo no LocalStorage, então utiliza ele.
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} to - Rota de destino.
|
|
19
|
+
* @param {Object} _from - Rota de origem.
|
|
20
|
+
* @param {Function} next - Função de redirecionamento.
|
|
21
|
+
* @param {Array} queryList='company' - Lista de filtros a serem aplicados.
|
|
22
|
+
*/
|
|
23
|
+
export function setDefaultFiltersBeforeEnter (to, _from, next, queryList = ['company']) {
|
|
24
|
+
const { getDefaultFiltersFromStorage, setFilterQuery } = useDefaultFilters()
|
|
25
|
+
|
|
26
|
+
const { query } = to
|
|
27
|
+
const newQuery = { ...query }
|
|
28
|
+
|
|
29
|
+
// recupera os filtros padrão do LocalStorage
|
|
30
|
+
const defaultFiltersFromStorage = getDefaultFiltersFromStorage()
|
|
31
|
+
|
|
32
|
+
queryList.forEach(name => {
|
|
33
|
+
// 1. se o filtro já estiver salvo no estado, então utiliza ele
|
|
34
|
+
if (filterQuery.value[name]) {
|
|
35
|
+
newQuery[name] = filterQuery.value[name]
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 2. se o filtro já estiver na query, então utiliza ele
|
|
40
|
+
if (query[name]) {
|
|
41
|
+
setFilterQuery(query[name], name)
|
|
42
|
+
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const storedFilter = defaultFiltersFromStorage[name]
|
|
47
|
+
|
|
48
|
+
// 3. se o filtro já estiver salvo no LocalStorage, então utiliza ele
|
|
49
|
+
if (storedFilter) {
|
|
50
|
+
setFilterQuery(storedFilter, name)
|
|
51
|
+
newQuery[name] = storedFilter
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Verifica se houve mudanças na query antes de redirecionar, sem essa validação
|
|
57
|
+
* o redirecionamento ocorre mesmo que a query seja a mesma, gerando loop infinito.
|
|
58
|
+
*/
|
|
59
|
+
if (!is.deepEqual(newQuery, query)) return next({ ...to, query: newQuery, replace: true })
|
|
60
|
+
|
|
61
|
+
next()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Este composable recupera os filtros default salvos no LocalStorage na chave 'defaultFilters'.
|
|
66
|
+
*/
|
|
67
|
+
export default function useDefaultFilters () {
|
|
68
|
+
const hasFilterQuery = computed(() => !!Object.keys(filterQuery.value).length)
|
|
69
|
+
|
|
70
|
+
function setFilterQuery (query, name = 'company') {
|
|
71
|
+
filterQuery.value[name] = query
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getDefaultFiltersFromStorage () {
|
|
75
|
+
const defaultFiltersFromStorage = LocalStorage.getItem('defaultFilters') || {}
|
|
76
|
+
|
|
77
|
+
return defaultFiltersFromStorage
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function triggerDefaultFiltersChange (newFilters, oldFilters) {
|
|
81
|
+
// necessário verificar se houve mudanças antes de disparar o evento para não duplicar.
|
|
82
|
+
if (is.deepEqual(newFilters, oldFilters)) return
|
|
83
|
+
|
|
84
|
+
defaultFiltersHooks.defaultFiltersChange.forEach(hook => hook(newFilters, oldFilters))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function onDefaultFiltersChange (callback) {
|
|
88
|
+
defaultFiltersHooks.defaultFiltersChange.push(callback)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function removeOnDefaultFiltersChange (callback) {
|
|
92
|
+
const index = defaultFiltersHooks.defaultFiltersChange.indexOf(callback)
|
|
93
|
+
|
|
94
|
+
if (~index) defaultFiltersHooks.defaultFiltersChange.splice(index, 1)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
filterQuery,
|
|
99
|
+
hasFilterQuery,
|
|
100
|
+
getDefaultFiltersFromStorage,
|
|
101
|
+
onDefaultFiltersChange,
|
|
102
|
+
removeOnDefaultFiltersChange,
|
|
103
|
+
setFilterQuery,
|
|
104
|
+
triggerDefaultFiltersChange
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -4,6 +4,8 @@ import hasParentByClassName from '../helpers/private/has-parent-by-class-name'
|
|
|
4
4
|
import { Notify } from 'quasar'
|
|
5
5
|
import { ref } from 'vue'
|
|
6
6
|
|
|
7
|
+
import naveNotificationSound from '../assets/sounds/nave-notification.mp3'
|
|
8
|
+
|
|
7
9
|
const callbackFunctions = {
|
|
8
10
|
onNotificationReceived: []
|
|
9
11
|
}
|
|
@@ -63,6 +65,8 @@ export default function () {
|
|
|
63
65
|
timeout: 30000
|
|
64
66
|
})
|
|
65
67
|
|
|
68
|
+
sendNotificationSound()
|
|
69
|
+
|
|
66
70
|
/**
|
|
67
71
|
* Função que é chamada quando o usuário clica na notificação, se a notificação
|
|
68
72
|
* tem link, então ele vai ser redirecionado para o link em uma nova aba, caso
|
|
@@ -96,6 +100,16 @@ export default function () {
|
|
|
96
100
|
</div>
|
|
97
101
|
`)
|
|
98
102
|
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Função que toca o som de notificação.
|
|
106
|
+
*/
|
|
107
|
+
function sendNotificationSound () {
|
|
108
|
+
const audio = new Audio(naveNotificationSound)
|
|
109
|
+
|
|
110
|
+
// o áudio agora é reproduzível; reproduza-o se as permissões permitirem
|
|
111
|
+
audio.addEventListener('canplaythrough', audio.play)
|
|
112
|
+
}
|
|
99
113
|
}
|
|
100
114
|
|
|
101
115
|
return {
|