@bildvitta/quasar-ui-asteroid 3.15.0-beta.7 → 3.15.0-beta.9
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-menu/QasActionsMenu.vue +124 -106
- package/src/components/actions-menu/QasActionsMenu.yml +6 -2
- package/src/components/actions-menu/composables/use-delete.js +30 -0
- package/src/components/actions-menu/composables/use-double-split-actions.js +42 -0
- package/src/components/actions-menu/composables/use-options-actions.js +28 -0
- package/src/components/actions-menu/composables/use-single-action.js +17 -0
- package/src/components/actions-menu/composables/use-single-split-actions.js +35 -0
- package/src/components/actions-menu/composables/use-tooltips.js +43 -0
- package/src/components/actions-menu/utils/get-label.js +3 -0
- package/src/components/actions-menu/utils/set-click-handler.js +6 -0
- package/src/components/app-menu/QasAppMenu.vue +21 -2
- package/src/components/app-user/QasAppUser.vue +14 -0
- package/src/components/btn-dropdown/QasBtnDropdown.vue +42 -44
- package/src/components/btn-dropdown/QasBtnDropdown.yml +13 -9
- package/src/components/uploader/QasUploader.vue +9 -1
- package/src/components/uploader/QasUploader.yml +4 -0
- package/src/composables/use-query-cache.js +9 -2
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div v-if="
|
|
3
|
-
<
|
|
4
|
-
<q-list
|
|
5
|
-
<slot v-for="(item, key) in
|
|
6
|
-
<q-item v-bind="item
|
|
2
|
+
<div v-if="hasList" class="qas-actions-menu" data-cy="actions-menu">
|
|
3
|
+
<qas-btn-dropdown v-bind="btnDropdownProps">
|
|
4
|
+
<q-list data-cy="actions-menu-list">
|
|
5
|
+
<slot v-for="(item, key) in formattedList.dropdownList" :item="item" :name="key">
|
|
6
|
+
<q-item v-bind="getItemProps(item)" :key="key" clickable data-cy="actions-menu-list-item" @click="setClickHandler(item)">
|
|
7
7
|
<q-item-section avatar>
|
|
8
8
|
<q-icon :name="item.icon" />
|
|
9
9
|
</q-item-section>
|
|
@@ -17,21 +17,35 @@
|
|
|
17
17
|
</slot>
|
|
18
18
|
</q-list>
|
|
19
19
|
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
<template v-for="(item, name) in tooltipLabels" :key="name" #[`bottom-${name}`]>
|
|
21
|
+
<q-tooltip v-if="showTooltip(item)">
|
|
22
|
+
{{ item.label }}
|
|
23
|
+
</q-tooltip>
|
|
24
|
+
</template>
|
|
25
|
+
</qas-btn-dropdown>
|
|
24
26
|
</div>
|
|
25
27
|
</template>
|
|
26
28
|
|
|
27
29
|
<script setup>
|
|
28
|
-
import QasBtn from '../btn/QasBtn.vue'
|
|
29
30
|
import QasBtnDropdown from '../btn-dropdown/QasBtnDropdown.vue'
|
|
30
31
|
|
|
31
32
|
import useScreen from '../../composables/use-screen'
|
|
32
33
|
|
|
34
|
+
import useDelete from './composables/use-delete'
|
|
35
|
+
import useTooltips from './composables/use-tooltips'
|
|
36
|
+
import useOptionsActions from './composables/use-options-actions'
|
|
37
|
+
import useSingleAction from './composables/use-single-action'
|
|
38
|
+
import useDoubleSplitActions from './composables/use-double-split-actions'
|
|
39
|
+
import useSingleSplitActions from './composables/use-single-split-actions'
|
|
40
|
+
|
|
41
|
+
import getLabel from './utils/get-label'
|
|
42
|
+
import setClickHandler from './utils/set-click-handler'
|
|
43
|
+
|
|
33
44
|
import { computed, inject } from 'vue'
|
|
34
45
|
|
|
46
|
+
const DEFAULT_COLOR = 'grey-10'
|
|
47
|
+
const SPLIT_SIZE = 2
|
|
48
|
+
|
|
35
49
|
defineOptions({ name: 'QasActionsMenu' })
|
|
36
50
|
|
|
37
51
|
const qas = inject('qas')
|
|
@@ -42,6 +56,10 @@ const props = defineProps({
|
|
|
42
56
|
type: Object
|
|
43
57
|
},
|
|
44
58
|
|
|
59
|
+
disable: {
|
|
60
|
+
type: Boolean
|
|
61
|
+
},
|
|
62
|
+
|
|
45
63
|
deleteLabel: {
|
|
46
64
|
default: 'Excluir',
|
|
47
65
|
type: String
|
|
@@ -57,11 +75,6 @@ const props = defineProps({
|
|
|
57
75
|
type: Object
|
|
58
76
|
},
|
|
59
77
|
|
|
60
|
-
dropdownIcon: {
|
|
61
|
-
default: 'sym_r_more_vert',
|
|
62
|
-
type: String
|
|
63
|
-
},
|
|
64
|
-
|
|
65
78
|
list: {
|
|
66
79
|
default: () => ({}),
|
|
67
80
|
type: Object
|
|
@@ -83,8 +96,33 @@ const props = defineProps({
|
|
|
83
96
|
})
|
|
84
97
|
|
|
85
98
|
const screen = useScreen()
|
|
86
|
-
const { deleteBtnProps, hasDelete } = useDelete()
|
|
87
99
|
|
|
100
|
+
const { deleteBtnProps, hasDelete } = useDelete({ color: DEFAULT_COLOR, props, qas })
|
|
101
|
+
|
|
102
|
+
const hasSplitName = computed(() => !!props.splitName)
|
|
103
|
+
const hasList = computed(() => !!Object.keys(fullList.value).length)
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Tamanho total da lista, considerando ação de deletar caso exista.
|
|
107
|
+
*/
|
|
108
|
+
const listSize = computed(() => Object.keys(fullList.value).length)
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Só existe split caso tenha a propriedade "splitName" e o tamanho da lista seja
|
|
112
|
+
* maior que o tamanho permitido no SPLIT_SIZE (no caso 2).
|
|
113
|
+
*/
|
|
114
|
+
const hasSplit = computed(() => hasSplitName.value && listSize.value > SPLIT_SIZE)
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Quando existe apenas i item na lista, neste caso é mostrado o botão direto,
|
|
118
|
+
* mesmo que não tenha a propriedade "splitName".
|
|
119
|
+
*/
|
|
120
|
+
const isSingle = computed(() => Object.keys(fullList.value).length === 1)
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Lista englobando as que vem por propriedade "list" mergeadas com a do
|
|
124
|
+
* delete, caso exista.
|
|
125
|
+
*/
|
|
88
126
|
const fullList = computed(() => {
|
|
89
127
|
return {
|
|
90
128
|
...props.list,
|
|
@@ -92,123 +130,103 @@ const fullList = computed(() => {
|
|
|
92
130
|
}
|
|
93
131
|
})
|
|
94
132
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
133
|
+
/**
|
|
134
|
+
* chave do primaria do objeto, é considerado sendo a splitName caso exista no
|
|
135
|
+
* objeto, porém pode acontecer deste item deixar de existir por algum motivo
|
|
136
|
+
* então consideramos a chave primaria sendo o primeiro item da computada fullList.
|
|
137
|
+
*/
|
|
138
|
+
const primaryKey = computed(() => {
|
|
139
|
+
return props.splitName in fullList.value ? props.splitName : Object.keys(fullList.value)?.[0]
|
|
140
|
+
})
|
|
100
141
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
142
|
+
const btnDropdownProps = computed(() => {
|
|
143
|
+
return {
|
|
144
|
+
buttonsPropsList: formattedList.value.buttonsList,
|
|
145
|
+
disable: props.disable,
|
|
146
|
+
useSplit: hasSplit.value
|
|
105
147
|
}
|
|
106
|
-
|
|
107
|
-
return list
|
|
108
148
|
})
|
|
109
149
|
|
|
110
|
-
const
|
|
111
|
-
const
|
|
150
|
+
const primaryButtonProps = computed(() => {
|
|
151
|
+
const buttonProps = fullList.value[primaryKey.value] || {}
|
|
112
152
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
153
|
+
return {
|
|
154
|
+
/**
|
|
155
|
+
* Caso seja "isSingle" e tenha a opção de deletar, significa que único botão
|
|
156
|
+
* que existe é o de deletar, então a cor precisa ser "DEFAULT_COLOR", em todos
|
|
157
|
+
* os outros cenários, é primary.
|
|
158
|
+
*/
|
|
159
|
+
color: isSingle.value && hasDelete.value ? DEFAULT_COLOR : 'primary',
|
|
118
160
|
|
|
119
|
-
|
|
120
|
-
const defaultButtonProps = computed(() => {
|
|
121
|
-
const { label, variant, ...buttonProps } = props.buttonProps
|
|
161
|
+
...buttonProps,
|
|
122
162
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
useLabelOnSmallScreen: false,
|
|
126
|
-
...buttonProps
|
|
163
|
+
label: getLabel({ useLabel: props.useLabel, label: buttonProps.label }),
|
|
164
|
+
onClick: () => setClickHandler(buttonProps)
|
|
127
165
|
}
|
|
128
166
|
})
|
|
129
167
|
|
|
130
|
-
const
|
|
131
|
-
const
|
|
168
|
+
const formattedList = computed(() => {
|
|
169
|
+
const buttonsPropsList = { ...fullList.value }
|
|
132
170
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
171
|
+
/**
|
|
172
|
+
* dropdownList: lista que ficará no menu dropdown.
|
|
173
|
+
* buttonsList: lista de botões que ficará **fora** do menu dropdown.
|
|
174
|
+
*/
|
|
175
|
+
const payload = { dropdownList: {}, buttonsList: {} }
|
|
137
176
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
...(props.useLabel && { label: hasSplit.value ? label : 'Opções' }),
|
|
141
|
-
...defaultBtnProps,
|
|
142
|
-
icon: icon || defaultIcon
|
|
143
|
-
},
|
|
177
|
+
if ((!hasSplitName.value || screen.isSmall) && !isSingle.value) {
|
|
178
|
+
const { buttonsList } = useOptionsActions({ color: DEFAULT_COLOR, props })
|
|
144
179
|
|
|
145
|
-
|
|
146
|
-
|
|
180
|
+
payload.buttonsList = buttonsList.value
|
|
181
|
+
payload.dropdownList = buttonsPropsList
|
|
147
182
|
|
|
148
|
-
|
|
183
|
+
return payload
|
|
149
184
|
}
|
|
150
|
-
})
|
|
151
185
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const { color: defaultColor, ...defaultBtnProps } = defaultButtonProps.value
|
|
186
|
+
if (isSingle.value) {
|
|
187
|
+
const { buttonsList } = useSingleAction({ primaryButtonProps, primaryKey })
|
|
155
188
|
|
|
156
|
-
|
|
157
|
-
color: color || defaultColor,
|
|
158
|
-
icon,
|
|
159
|
-
label: props.useLabel ? tooltipLabel.value : '',
|
|
160
|
-
onClick,
|
|
161
|
-
...defaultBtnProps
|
|
162
|
-
}
|
|
163
|
-
})
|
|
189
|
+
payload.buttonsList = buttonsList.value
|
|
164
190
|
|
|
165
|
-
|
|
191
|
+
return payload
|
|
192
|
+
}
|
|
166
193
|
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
194
|
+
if (listSize.value === SPLIT_SIZE) {
|
|
195
|
+
const { buttonsList } = useDoubleSplitActions({
|
|
196
|
+
buttonsPropsList,
|
|
197
|
+
color: DEFAULT_COLOR,
|
|
198
|
+
primaryButtonProps,
|
|
199
|
+
primaryKey,
|
|
200
|
+
props
|
|
201
|
+
})
|
|
170
202
|
|
|
171
|
-
|
|
172
|
-
is,
|
|
173
|
-
props: {
|
|
174
|
-
...(isBtnDropdown.value ? btnDropdownProps.value : btnProps.value),
|
|
175
|
-
...(hasDelete.value && props.deleteProps)
|
|
176
|
-
}
|
|
203
|
+
payload.buttonsList = buttonsList.value
|
|
177
204
|
}
|
|
178
|
-
})
|
|
179
205
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
206
|
+
if (listSize.value > SPLIT_SIZE) {
|
|
207
|
+
const { list } = useSingleSplitActions({
|
|
208
|
+
buttonsPropsList,
|
|
209
|
+
primaryButtonProps,
|
|
210
|
+
primaryKey,
|
|
211
|
+
props
|
|
212
|
+
})
|
|
185
213
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
item.handler(filtered)
|
|
214
|
+
payload.buttonsList = list.value.buttonsList
|
|
215
|
+
payload.dropdownList = list.value.dropdownList
|
|
189
216
|
}
|
|
190
|
-
}
|
|
191
217
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
color: 'grey-10',
|
|
201
|
-
icon: props.deleteIcon,
|
|
202
|
-
label: props.deleteLabel,
|
|
203
|
-
handler: () => qas.delete(props.deleteProps)
|
|
204
|
-
}
|
|
205
|
-
})
|
|
206
|
-
}
|
|
207
|
-
})
|
|
218
|
+
return payload
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
const { showTooltip, tooltipLabels } = useTooltips({ formattedList, fullList, props })
|
|
222
|
+
|
|
223
|
+
// functions
|
|
224
|
+
function getItemProps (item) {
|
|
225
|
+
const { disable, props: itemProps } = item
|
|
208
226
|
|
|
209
227
|
return {
|
|
210
|
-
|
|
211
|
-
|
|
228
|
+
disable,
|
|
229
|
+
...itemProps
|
|
212
230
|
}
|
|
213
231
|
}
|
|
214
232
|
</script>
|
|
@@ -5,10 +5,14 @@ meta:
|
|
|
5
5
|
|
|
6
6
|
props:
|
|
7
7
|
button-props:
|
|
8
|
-
desc: Propriedades
|
|
8
|
+
desc: Propriedades que são usadas no modo "Opções", que é quando não é usado a propriedade "splitName" ou esteja no mobile;
|
|
9
9
|
default: {}
|
|
10
10
|
type: Object
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
disable:
|
|
13
|
+
desc: Desabilita o componente como um todo.
|
|
14
|
+
default: false
|
|
15
|
+
type: Boolean
|
|
12
16
|
|
|
13
17
|
delete-icon:
|
|
14
18
|
desc: Ícone do botão de deletar.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { computed } from 'vue'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {{
|
|
5
|
+
* color: string,
|
|
6
|
+
* props: { deleteIcon: string, deleteLabel: string, deleteProps: object },
|
|
7
|
+
* qas: { delete: function(object) }
|
|
8
|
+
* }}
|
|
9
|
+
*/
|
|
10
|
+
export default function useDelete ({ color, props, qas }) {
|
|
11
|
+
const hasDelete = computed(() => !!Object.keys(props.deleteProps).length)
|
|
12
|
+
|
|
13
|
+
const deleteBtnProps = computed(() => {
|
|
14
|
+
return {
|
|
15
|
+
...(hasDelete.value && {
|
|
16
|
+
delete: {
|
|
17
|
+
color,
|
|
18
|
+
icon: props.deleteIcon,
|
|
19
|
+
label: props.deleteLabel,
|
|
20
|
+
handler: () => qas.delete(props.deleteProps)
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
deleteBtnProps,
|
|
28
|
+
hasDelete
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import setClickHandler from '../utils/set-click-handler'
|
|
2
|
+
import getLabel from '../utils/get-label'
|
|
3
|
+
|
|
4
|
+
import { computed } from 'vue'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Cenário onde ficará um botão ao lado do outro "Ação primaria | Ação secundaria".
|
|
8
|
+
*
|
|
9
|
+
* @param {{
|
|
10
|
+
* color: string,
|
|
11
|
+
* props: { useLabel: boolean },
|
|
12
|
+
* primaryKey: import('vue').ComputedRef<string>,
|
|
13
|
+
* buttonsPropsList: import('vue').ComputedRef<object>
|
|
14
|
+
* primaryButtonProps: import('vue').ComputedRef<primaryButtonProps>
|
|
15
|
+
* }} config
|
|
16
|
+
*/
|
|
17
|
+
export default function useDoubleSplitActions (config = {}) {
|
|
18
|
+
const { color, props, primaryKey, buttonsPropsList, primaryButtonProps } = config
|
|
19
|
+
|
|
20
|
+
const buttonsList = computed(() => {
|
|
21
|
+
const secondaryKey = Object.keys(buttonsPropsList).find(key => key !== primaryKey.value)
|
|
22
|
+
const secondaryButton = buttonsPropsList[secondaryKey]
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
[primaryKey.value]: {
|
|
26
|
+
...primaryButtonProps.value,
|
|
27
|
+
label: getLabel({ useLabel: props.useLabel, label: primaryButtonProps.value.label })
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
[secondaryKey]: {
|
|
31
|
+
...secondaryButton,
|
|
32
|
+
color,
|
|
33
|
+
label: getLabel({ useLabel: props.useLabel, label: secondaryButton.label }),
|
|
34
|
+
onClick: () => setClickHandler(secondaryButton)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
buttonsList
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import getLabel from '../utils/get-label'
|
|
2
|
+
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cenário onde não é utilizado com split e tenha mais de 1 item na lista,
|
|
7
|
+
* neste caso, ele se torna o botão de "opções" onde será usado em casos como
|
|
8
|
+
* dentro de cards, etc.
|
|
9
|
+
*
|
|
10
|
+
* @param {{ color: string, props: { buttonProps: object, useLabel: boolean } }}
|
|
11
|
+
*/
|
|
12
|
+
export default function useOptionsActions ({ props, color }) {
|
|
13
|
+
const buttonsList = computed(() => {
|
|
14
|
+
return {
|
|
15
|
+
options: {
|
|
16
|
+
color,
|
|
17
|
+
iconRight: 'sym_r_more_vert',
|
|
18
|
+
useLabelOnSmallScreen: false,
|
|
19
|
+
...props.buttonProps,
|
|
20
|
+
label: getLabel({ useLabel: props.useLabel, label: 'Opções' }) // label não pode ser sobrescrita.
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
buttonsList
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { computed } from 'vue'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cenário para quando existir apenas 1 item na lista.
|
|
5
|
+
*
|
|
6
|
+
* @param {{
|
|
7
|
+
* primaryKey: import('vue').ComputedRef<string>,
|
|
8
|
+
* primaryButtonProps: import('vue').ComputedRef<primaryButtonProps>
|
|
9
|
+
* }}
|
|
10
|
+
*/
|
|
11
|
+
export default function useSingleAction ({ primaryKey, primaryButtonProps }) {
|
|
12
|
+
const buttonsList = computed(() => ({ [primaryKey.value]: { ...primaryButtonProps.value } }))
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
buttonsList
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { computed } from 'vue'
|
|
2
|
+
|
|
3
|
+
import getLabel from '../utils/get-label'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cenário onde vai ter mais de 2 itens na lista, então precisa ficar
|
|
7
|
+
* o botão primário fora do dropdown e o restante dentro do dropdown.
|
|
8
|
+
*
|
|
9
|
+
* @param {{
|
|
10
|
+
* props: { useLabel: boolean },
|
|
11
|
+
* primaryKey: import('vue').ComputedRef<string>,
|
|
12
|
+
* buttonsPropsList: import('vue').ComputedRef<object>
|
|
13
|
+
* primaryButtonProps: import('vue').ComputedRef<primaryButtonProps>
|
|
14
|
+
* }} config
|
|
15
|
+
*/
|
|
16
|
+
export default function useSingleSplitActions ({ buttonsPropsList, props, primaryKey, primaryButtonProps }) {
|
|
17
|
+
delete buttonsPropsList[primaryKey.value]
|
|
18
|
+
|
|
19
|
+
const list = computed(() => {
|
|
20
|
+
return {
|
|
21
|
+
buttonsList: {
|
|
22
|
+
[primaryKey.value]: {
|
|
23
|
+
...primaryButtonProps.value,
|
|
24
|
+
label: getLabel({ useLabel: props.useLabel, label: primaryButtonProps.value.label })
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
dropdownList: buttonsPropsList
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
list
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { computed } from 'vue'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Composable responsável pelo tooltip
|
|
5
|
+
*
|
|
6
|
+
* @param {{
|
|
7
|
+
* props: { useTooltip: boolean, useLabel: boolean },
|
|
8
|
+
* formattedList: import('vue').ComputedRef<object>
|
|
9
|
+
* fullList: import('vue').ComputedRef<object>
|
|
10
|
+
* }}
|
|
11
|
+
*/
|
|
12
|
+
export default function useTooltip ({ props, formattedList, fullList }) {
|
|
13
|
+
const hasTooltip = computed(() => !props.useLabel && props.useTooltip)
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Precisa existir um tooltip para item de "formattedList.value.buttonsList",
|
|
17
|
+
* então é preciso fazer um tratamento, uma vez que dentro de "formattedList.value.buttonsList"
|
|
18
|
+
* não existe mais "labels", então recuperamos elas de "fullList".
|
|
19
|
+
*/
|
|
20
|
+
const tooltipLabels = computed(() => {
|
|
21
|
+
if (!hasTooltip.value) return {}
|
|
22
|
+
|
|
23
|
+
const formattedProps = {}
|
|
24
|
+
|
|
25
|
+
for (const key in formattedList.value.buttonsList) {
|
|
26
|
+
formattedProps[key] = {
|
|
27
|
+
label: fullList.value[key]?.label
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return formattedProps
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
function showTooltip ({ label }) {
|
|
35
|
+
return hasTooltip.value && label
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
tooltipLabels,
|
|
40
|
+
|
|
41
|
+
showTooltip
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="qas-app-menu">
|
|
3
|
-
<q-drawer v-model="model" :behavior="behavior" class="shadow-2" :mini="isMiniMode" :mini-width="88" show-if-above :width="drawerWidth" @mouseenter="onMouseEvent" @mouseleave="onMouseEvent">
|
|
3
|
+
<q-drawer :key="reRenderCount" v-model="model" :behavior="behavior" class="shadow-2" :mini="isMiniMode" :mini-width="88" show-if-above :width="drawerWidth" @mouseenter="onMouseEvent" @mouseleave="onMouseEvent">
|
|
4
4
|
<div class="column full-height justify-between no-wrap">
|
|
5
5
|
<div class="full-width">
|
|
6
6
|
<!-- Brand -->
|
|
@@ -93,7 +93,7 @@ import useAppUser from './composables/use-app-user'
|
|
|
93
93
|
import useDevelopmentBadge from './composables/use-development-badge'
|
|
94
94
|
import { useScreen } from '../../composables'
|
|
95
95
|
|
|
96
|
-
import { ref, computed } from 'vue'
|
|
96
|
+
import { ref, computed, watch } from 'vue'
|
|
97
97
|
import { useRouter } from 'vue-router'
|
|
98
98
|
|
|
99
99
|
defineOptions({
|
|
@@ -155,6 +155,7 @@ const rootRoute = router.hasRoute('Root') ? { name: 'Root' } : { path: '/' }
|
|
|
155
155
|
|
|
156
156
|
const hasOpenedMenu = ref(false)
|
|
157
157
|
const isMini = ref(screen.isLarge)
|
|
158
|
+
const reRenderCount = ref(0)
|
|
158
159
|
|
|
159
160
|
const composableParams = {
|
|
160
161
|
props,
|
|
@@ -194,6 +195,24 @@ const classes = computed(() => {
|
|
|
194
195
|
}
|
|
195
196
|
})
|
|
196
197
|
|
|
198
|
+
/**
|
|
199
|
+
* @desc Recurso tecnológico temporário (ou definitivo), este bug ocorre por conta
|
|
200
|
+
* da atualização do vue para a versão `v3.4+`, onde tiveram mudanças referentes a
|
|
201
|
+
* reatividade, existem issues abertas no Quasar, porém sem expectativas
|
|
202
|
+
* de que um dia será resolvido por parte deles.
|
|
203
|
+
*
|
|
204
|
+
* @see {@link https://github.com/quasarframework/quasar/issues/16651}
|
|
205
|
+
*/
|
|
206
|
+
watch(() => behavior.value, value => {
|
|
207
|
+
/**
|
|
208
|
+
* @desc quando o comportamento passa a ser desktop novamente é porque aconteceu um
|
|
209
|
+
* resize na pagina, então é necessário renderizar o componente QDrawer novamente.
|
|
210
|
+
*/
|
|
211
|
+
if (value === 'desktop') {
|
|
212
|
+
reRenderCount.value += 1
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
|
|
197
216
|
// métodos
|
|
198
217
|
function closeDrawer () {
|
|
199
218
|
emit('update:modelValue', false)
|
|
@@ -72,9 +72,11 @@
|
|
|
72
72
|
import QasAvatar from '../avatar/QasAvatar.vue'
|
|
73
73
|
|
|
74
74
|
import useNotifications from '../../composables/use-notifications'
|
|
75
|
+
import useQueryCache from '../../composables/use-query-cache'
|
|
75
76
|
import { NotifySuccess, NotifyError } from '../../plugins'
|
|
76
77
|
|
|
77
78
|
import { ref, computed, watch, inject } from 'vue'
|
|
79
|
+
import { useRouter } from 'vue-router'
|
|
78
80
|
|
|
79
81
|
defineOptions({ name: 'QasAppUser' })
|
|
80
82
|
|
|
@@ -111,8 +113,12 @@ const emit = defineEmits(['sign-out', 'toggle-notifications'])
|
|
|
111
113
|
// vindo direto do boot api.js
|
|
112
114
|
const axios = inject('axios')
|
|
113
115
|
|
|
116
|
+
const router = useRouter()
|
|
117
|
+
|
|
114
118
|
const { isNotificationsEnabled, unreadNotificationsCount } = useNotifications()
|
|
115
119
|
|
|
120
|
+
const { reset } = useQueryCache()
|
|
121
|
+
|
|
116
122
|
const companiesModel = ref('')
|
|
117
123
|
const loading = ref(false)
|
|
118
124
|
|
|
@@ -198,9 +204,17 @@ async function setCompanies (value) {
|
|
|
198
204
|
NotifyError('Falha ao alterar vínculo.')
|
|
199
205
|
} finally {
|
|
200
206
|
loading.value = false
|
|
207
|
+
|
|
208
|
+
clearCachedFilters()
|
|
201
209
|
}
|
|
202
210
|
}
|
|
203
211
|
|
|
212
|
+
function clearCachedFilters () {
|
|
213
|
+
reset()
|
|
214
|
+
|
|
215
|
+
router.push({ query: {} })
|
|
216
|
+
}
|
|
217
|
+
|
|
204
218
|
function onMenuHide () {
|
|
205
219
|
if (!companiesModel.value) {
|
|
206
220
|
companiesModel.value = props.companyProps.modelValue
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="qas-btn-dropdown" :class="classes.parent">
|
|
3
|
-
<div v-if="
|
|
4
|
-
<
|
|
5
|
-
<
|
|
6
|
-
<
|
|
7
|
-
<
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
<div v-if="hasButtons" class="flex">
|
|
4
|
+
<div v-for="(buttonProps, key, index) in props.buttonsPropsList" :key="key">
|
|
5
|
+
<div class="flex">
|
|
6
|
+
<qas-btn :disable="props.disable" v-bind="buttonProps" variant="tertiary" @click="onClick">
|
|
7
|
+
<q-menu v-if="hasMenuOnLeftSide" v-model="isMenuOpened" anchor="bottom right" auto-close self="top right" @update:model-value="onUpdateMenuValue">
|
|
8
|
+
<div :class="classes.menuContent">
|
|
9
|
+
<slot />
|
|
10
|
+
</div>
|
|
11
|
+
</q-menu>
|
|
12
|
+
</qas-btn>
|
|
13
|
+
|
|
14
|
+
<slot :name="`bottom-${key}`" />
|
|
15
|
+
|
|
16
|
+
<q-separator v-if="hasSeparator(index)" class="q-mx-sm qas-btn-dropdown__separator self-center" dark vertical />
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
13
19
|
</div>
|
|
14
20
|
|
|
15
|
-
<q-separator v-if="hasSeparator" class="q-mr-sm qas-btn-dropdown__separator self-center" dark vertical />
|
|
16
|
-
|
|
17
21
|
<div v-if="props.useSplit">
|
|
18
|
-
<qas-btn color="grey-10" :icon="props.dropdownIcon" variant="tertiary">
|
|
22
|
+
<qas-btn color="grey-10" :disable="disable" :icon="props.dropdownIcon" variant="tertiary">
|
|
19
23
|
<q-menu v-if="hasDefaultSlot" anchor="bottom right" auto-close self="top right">
|
|
20
24
|
<div :class="classes.menuContent">
|
|
21
25
|
<slot />
|
|
@@ -37,7 +41,7 @@ defineOptions({
|
|
|
37
41
|
})
|
|
38
42
|
|
|
39
43
|
const props = defineProps({
|
|
40
|
-
|
|
44
|
+
buttonsPropsList: {
|
|
41
45
|
default: () => ({}),
|
|
42
46
|
type: Object
|
|
43
47
|
},
|
|
@@ -47,7 +51,11 @@ const props = defineProps({
|
|
|
47
51
|
type: String
|
|
48
52
|
},
|
|
49
53
|
|
|
50
|
-
|
|
54
|
+
disable: {
|
|
55
|
+
type: Boolean
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
menu: {
|
|
51
59
|
type: Boolean
|
|
52
60
|
},
|
|
53
61
|
|
|
@@ -55,8 +63,13 @@ const props = defineProps({
|
|
|
55
63
|
type: Boolean
|
|
56
64
|
},
|
|
57
65
|
|
|
58
|
-
|
|
66
|
+
useSplit: {
|
|
59
67
|
type: Boolean
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
useTooltip: {
|
|
71
|
+
type: Boolean,
|
|
72
|
+
default: true
|
|
60
73
|
}
|
|
61
74
|
})
|
|
62
75
|
|
|
@@ -67,47 +80,24 @@ const screen = useScreen()
|
|
|
67
80
|
|
|
68
81
|
const isMenuOpened = ref(false)
|
|
69
82
|
|
|
70
|
-
const defaultButtonProps = computed(() => {
|
|
71
|
-
const {
|
|
72
|
-
icon,
|
|
73
|
-
iconRight,
|
|
74
|
-
color,
|
|
75
|
-
...defaultProps
|
|
76
|
-
} = props.buttonProps
|
|
77
|
-
|
|
78
|
-
const defaultIconRight = iconRight || props.dropdownIcon
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
useLabelOnSmallScreen: false,
|
|
82
|
-
|
|
83
|
-
...defaultProps,
|
|
84
|
-
|
|
85
|
-
color: color || (!props.useSplit ? 'grey-10' : 'primary'),
|
|
86
|
-
...(!props.useSplit && { iconRight: defaultIconRight }),
|
|
87
|
-
...(props.useSplit && { icon })
|
|
88
|
-
}
|
|
89
|
-
})
|
|
90
|
-
|
|
91
83
|
const classes = computed(() => {
|
|
92
84
|
return {
|
|
93
85
|
parent: {
|
|
94
86
|
'flex inline items-center': props.useSplit
|
|
95
87
|
},
|
|
96
88
|
|
|
97
|
-
leftSide: {
|
|
98
|
-
'q-mr-sm': props.useSplit
|
|
99
|
-
},
|
|
100
|
-
|
|
101
89
|
menuContent: {
|
|
102
90
|
'q-pa-md': props.useMenuPadding
|
|
103
91
|
}
|
|
104
92
|
}
|
|
105
93
|
})
|
|
106
94
|
|
|
95
|
+
const buttonsPropsListSize = computed(() => Object.keys(props.buttonsPropsList).length)
|
|
96
|
+
const isSingleButton = computed(() => buttonsPropsListSize.value === 1)
|
|
97
|
+
|
|
98
|
+
const hasButtons = computed(() => !screen.isSmall || !props.useSplit)
|
|
107
99
|
const hasDefaultSlot = computed(() => !!slots.default)
|
|
108
|
-
const
|
|
109
|
-
const hasMenuOnLeftSide = computed(() => hasDefaultSlot.value && !props.useSplit)
|
|
110
|
-
const hasSeparator = computed(() => !screen.isSmall && props.useSplit)
|
|
100
|
+
const hasMenuOnLeftSide = computed(() => hasDefaultSlot.value && !props.useSplit && isSingleButton.value)
|
|
111
101
|
|
|
112
102
|
watch(() => props.menu, value => {
|
|
113
103
|
isMenuOpened.value = value
|
|
@@ -120,6 +110,14 @@ function onUpdateMenuValue (value) {
|
|
|
120
110
|
function onClick (event) {
|
|
121
111
|
emit('click', event)
|
|
122
112
|
}
|
|
113
|
+
|
|
114
|
+
function isLast (index) {
|
|
115
|
+
return index + 1 === buttonsPropsListSize.value
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function hasSeparator (index) {
|
|
119
|
+
return props.useSplit || !isLast(index)
|
|
120
|
+
}
|
|
123
121
|
</script>
|
|
124
122
|
|
|
125
123
|
<style lang="scss">
|
|
@@ -4,11 +4,16 @@ meta:
|
|
|
4
4
|
desc: Componente semelhante ao QBtnDropdown porém utilizando o QasBtn e QSeparator para aplicar estilos padrões do Design System.
|
|
5
5
|
|
|
6
6
|
props:
|
|
7
|
-
|
|
8
|
-
desc:
|
|
9
|
-
default:
|
|
10
|
-
type:
|
|
11
|
-
examples: ["{ color: 'white', icon: 'sym_r_person' }"]
|
|
7
|
+
buttons-props-list:
|
|
8
|
+
desc: Lista de propriedades repassadas para os botões.
|
|
9
|
+
default: []
|
|
10
|
+
type: Array
|
|
11
|
+
examples: ["[{ color: 'white', icon: 'sym_r_person' }]"]
|
|
12
|
+
|
|
13
|
+
disable:
|
|
14
|
+
desc: Desabilita o componente como um todo.
|
|
15
|
+
default: false
|
|
16
|
+
type: Boolean
|
|
12
17
|
|
|
13
18
|
dropdown-icon:
|
|
14
19
|
desc: Ícone a direita do dropdown.
|
|
@@ -33,10 +38,9 @@ props:
|
|
|
33
38
|
|
|
34
39
|
slots:
|
|
35
40
|
default:
|
|
36
|
-
desc: Slot para passar o conteúdo do dropdown (menu)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
desc: Slot para substituir o botão a esquerda.
|
|
41
|
+
desc: Slot para passar o conteúdo do dropdown (menu).
|
|
42
|
+
bottom-[buttons-props-list-key]:
|
|
43
|
+
desc: Slot unitário para acessar abaixo de cada botão (normalmente utilizado para tooltip).
|
|
40
44
|
|
|
41
45
|
events:
|
|
42
46
|
'@click -> function (event)':
|
|
@@ -489,7 +489,7 @@ export default {
|
|
|
489
489
|
if (file.isFailed) return
|
|
490
490
|
|
|
491
491
|
if (!this.isMultiple) {
|
|
492
|
-
return this.$emit('update:modelValue')
|
|
492
|
+
return this.$emit('update:modelValue', this.useObjectModel ? {} : '')
|
|
493
493
|
}
|
|
494
494
|
|
|
495
495
|
const clonedValue = extend(true, [], this.modelValue)
|
|
@@ -605,6 +605,14 @@ export default {
|
|
|
605
605
|
this.$emit('update:modelValue', this.isMultiple ? [...this.modelValue, model] : model || '')
|
|
606
606
|
|
|
607
607
|
this.updateUploading(false)
|
|
608
|
+
},
|
|
609
|
+
|
|
610
|
+
reset () {
|
|
611
|
+
this.$refs.uploader.reset()
|
|
612
|
+
|
|
613
|
+
const emptyModel = this.isMultiple ? [] : this.useObjectModel ? {} : ''
|
|
614
|
+
|
|
615
|
+
this.$emit('update:modelValue', emptyModel)
|
|
608
616
|
}
|
|
609
617
|
}
|
|
610
618
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SessionStorage } from 'quasar'
|
|
2
2
|
import { filterObject } from '../helpers'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
let cachedFilters = SessionStorage.getItem('cachedFilters') || {}
|
|
5
5
|
|
|
6
6
|
function updateSessionStorage () {
|
|
7
7
|
SessionStorage.set('cachedFilters', cachedFilters)
|
|
@@ -41,6 +41,12 @@ export default function () {
|
|
|
41
41
|
updateSessionStorage()
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
function reset () {
|
|
45
|
+
cachedFilters = {}
|
|
46
|
+
|
|
47
|
+
updateSessionStorage()
|
|
48
|
+
}
|
|
49
|
+
|
|
44
50
|
return {
|
|
45
51
|
addOne,
|
|
46
52
|
addMany,
|
|
@@ -48,6 +54,7 @@ export default function () {
|
|
|
48
54
|
findAll,
|
|
49
55
|
clearOne,
|
|
50
56
|
clearAll,
|
|
51
|
-
cachedFilters
|
|
57
|
+
cachedFilters,
|
|
58
|
+
reset
|
|
52
59
|
}
|
|
53
60
|
}
|