@bildvitta/quasar-ui-asteroid 3.7.0-beta.1 → 3.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bildvitta/quasar-ui-asteroid",
3
3
  "description": "Asteroid",
4
- "version": "3.7.0-beta.1",
4
+ "version": "3.7.0-beta.2",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "dist/asteroid.cjs.min.js",
@@ -41,7 +41,7 @@ export default {
41
41
  return [
42
42
  `justify-${this.align}`,
43
43
  `q-col-gutter-${this.defaultGutter}`,
44
- this.$qas.screen.isSmall ? 'column reverse' : 'row'
44
+ (this.$qas.screen.isSmall || this.useFullWidth) ? 'column reverse' : 'row'
45
45
  ]
46
46
  },
47
47
 
@@ -1,21 +1,19 @@
1
1
  <template>
2
- <div v-if="hasActions">
3
- <component :is="component.is" v-bind="component.props" :use-label-on-small-screen="useLabelOnSmallScreen" variant="tertiary" @click="onClick()">
4
- <q-menu v-if="hasMoreThanOneAction" auto-close class="q-py-xs">
5
- <q-list>
6
- <slot v-for="(item, key) in actions" :item="item" :name="key">
7
- <component :is="getComponent(key)" v-bind="item.props" :key="key" clickable @click="onClick(item)">
8
- <q-item-section avatar>
9
- <q-icon :name="item.icon" />
10
- </q-item-section>
11
-
12
- <q-item-section>
13
- <div>{{ item.label }}</div>
14
- </q-item-section>
15
- </component>
16
- </slot>
17
- </q-list>
18
- </q-menu>
2
+ <div v-if="hasActions" class="qas-actions-menu">
3
+ <component :is="component.is" v-bind="component.props" variant="tertiary">
4
+ <q-list v-if="isBtnDropdown">
5
+ <slot v-for="(item, key) in actions" :item="item" :name="key">
6
+ <q-item v-bind="item.props" :key="key" clickable @click="onClick(item)">
7
+ <q-item-section avatar>
8
+ <q-icon :name="item.icon" />
9
+ </q-item-section>
10
+
11
+ <q-item-section>
12
+ <div>{{ item.label }}</div>
13
+ </q-item-section>
14
+ </q-item>
15
+ </slot>
16
+ </q-list>
19
17
 
20
18
  <q-tooltip v-if="hasTooltip" class="text-caption">
21
19
  {{ tooltipLabel }}
@@ -26,28 +24,18 @@
26
24
 
27
25
  <script>
28
26
  import QasBtn from '../btn/QasBtn.vue'
29
- import QasDelete from '../delete/QasDelete.vue'
27
+ import QasBtnDropdown from '../btn-dropdown/QasBtnDropdown.vue'
30
28
 
31
29
  export default {
32
30
  name: 'QasActionsMenu',
33
31
 
34
32
  components: {
35
33
  QasBtn,
36
- QasDelete
34
+ QasBtnDropdown
37
35
  },
38
36
 
39
37
  props: {
40
- color: {
41
- default: '',
42
- type: String
43
- },
44
-
45
- icon: {
46
- default: 'sym_r_more_vert',
47
- type: String
48
- },
49
-
50
- list: {
38
+ buttonProps: {
51
39
  default: () => ({}),
52
40
  type: Object
53
41
  },
@@ -67,54 +55,63 @@ export default {
67
55
  type: Object
68
56
  },
69
57
 
70
- useLabel: {
71
- default: true,
72
- type: Boolean
58
+ dropdownIcon: {
59
+ default: 'sym_r_more_vert',
60
+ type: String
73
61
  },
74
62
 
75
- useLabelOnSmallScreen: {
63
+ list: {
64
+ default: () => ({}),
65
+ type: Object
66
+ },
67
+
68
+ splitName: {
69
+ type: String,
70
+ default: ''
71
+ },
72
+
73
+ useLabel: {
74
+ default: true,
76
75
  type: Boolean
77
76
  }
78
77
  },
79
78
 
80
79
  computed: {
81
80
  actions () {
81
+ const list = { ...this.fullList }
82
+
83
+ if (this.hasSplit && list[this.splitName] && this.isBtnDropdown) {
84
+ this.isSmall
85
+ ? Object.assign(list, { [this.splitName]: list[this.splitName] })
86
+ : delete list[this.splitName]
87
+ }
88
+
89
+ return list
90
+ },
91
+
92
+ fullList () {
82
93
  return {
83
94
  ...this.list,
84
95
  ...(this.hasDelete && {
85
96
  delete: {
97
+ color: 'grey-9',
86
98
  icon: this.deleteIcon,
87
99
  label: this.deleteLabel,
88
- props: {
89
- ...this.deleteProps,
90
- tag: this.hasMoreThanOneAction ? 'q-item' : 'qas-btn'
91
- }
100
+ handler: () => this.$qas.delete(this.deleteProps)
92
101
  }
93
102
  })
94
103
  }
95
104
  },
96
105
 
97
106
  component () {
98
- const props = {}
99
-
100
- // TODO: solução do color é temporária até ser definido o novo QasBtn.
101
- if (this.hasMoreThanOneAction) {
102
- props.color = this.color || 'grey-9'
103
- props.iconRight = this.icon
104
- props.label = this.useLabel ? 'Opções' : ''
105
- } else {
106
- const { color, icon } = this.actions[this.firstItemKey] || {}
107
-
108
- props.color = color || this.color || 'primary'
109
- props.icon = icon
110
- props.label = this.useLabel ? this.tooltipLabel : ''
111
- }
112
-
113
- this.hasDelete && Object.assign(props, this.deleteProps)
107
+ const is = this.isBtnDropdown ? 'qas-btn-dropdown' : 'qas-btn'
114
108
 
115
109
  return {
116
- is: this.hasMoreThanOneAction || !this.hasDelete ? 'qas-btn' : 'qas-delete',
117
- props
110
+ is,
111
+ props: {
112
+ ...(this.isBtnDropdown ? this.btnDropdownProps : this.btnProps),
113
+ ...(this.hasDelete && this.deleteProps)
114
+ }
118
115
  }
119
116
  },
120
117
 
@@ -130,26 +127,76 @@ export default {
130
127
  return !!Object.keys(this.deleteProps).length
131
128
  },
132
129
 
133
- hasMoreThanOneAction () {
134
- return Object.keys(this.list || {}).length + Number(this.hasDelete) > 1
130
+ hasSplit () {
131
+ return !!this.splitName
132
+ },
133
+
134
+ isBtnDropdown () {
135
+ return Object.keys(this.fullList || {}).length > 1
135
136
  },
136
137
 
137
138
  hasTooltip () {
138
- return !this.hasMoreThanOneAction && !this.useLabel
139
+ return !this.isBtnDropdown && !this.useLabel
139
140
  },
140
141
 
141
142
  tooltipLabel () {
142
143
  return this.actions[this.firstItemKey]?.label
144
+ },
145
+
146
+ defaultButtonProps () {
147
+ const { label, variant, ...buttonProps } = this.buttonProps
148
+
149
+ return {
150
+ useHoverOnWhiteColor: true,
151
+ useLabelOnSmallScreen: false,
152
+ ...buttonProps
153
+ }
154
+ },
155
+
156
+ btnDropdownProps () {
157
+ const { icon, label } = this.fullList[this.splitName] || {}
158
+
159
+ const {
160
+ icon: defaultIcon,
161
+ ...defaultButtonProps
162
+ } = this.defaultButtonProps
163
+
164
+ return {
165
+ buttonProps: {
166
+ ...(this.useLabel && { label: this.hasSplit ? label : 'Opções' }),
167
+ ...defaultButtonProps,
168
+ icon: icon || defaultIcon
169
+ },
170
+
171
+ dropdownIcon: this.dropdownIcon,
172
+ useSplit: this.hasSplit,
173
+
174
+ // evento
175
+ onClick: () => this.onClick(this.fullList[this.splitName])
176
+ }
177
+ },
178
+
179
+ btnProps () {
180
+ const { color, icon } = this.actions[this.firstItemKey] || {}
181
+ const { color: defaultColor, ...defaultButtonProps } = this.defaultButtonProps
182
+
183
+ return {
184
+ color: color || defaultColor,
185
+ icon,
186
+ label: this.useLabel ? this.tooltipLabel : '',
187
+ onClick: this.onClick,
188
+ ...defaultButtonProps
189
+ }
190
+ },
191
+
192
+ isSmall () {
193
+ return this.$qas.screen.isSmall
143
194
  }
144
195
  },
145
196
 
146
197
  methods: {
147
- getComponent (key) {
148
- return key === 'delete' ? 'qas-delete' : 'q-item'
149
- },
150
-
151
198
  onClick (item = {}) {
152
- if (!this.hasMoreThanOneAction) {
199
+ if (!this.isBtnDropdown) {
153
200
  item = this.actions[this.firstItemKey]
154
201
  }
155
202
 
@@ -4,9 +4,11 @@ meta:
4
4
  desc: Componente para abrir um menu de ação a partir de um botão, muito utilizado em tela de edição.
5
5
 
6
6
  props:
7
- color:
8
- desc: Cor do ícone.
9
- type: String
7
+ button-props:
8
+ desc: Propriedades repassadas para o "QasBtn" caso esteja em modo de botão (apenas um item na prop "list"), caso esteja em modo de dropdown (mais de 1 item na prop "list") é enviado para a propriedade buttonProps do "QasBtnDropdown".
9
+ default: {}
10
+ type: Object
11
+ examples: ["{ color: 'white', icon: 'sym_r_person', useLabelOnSmallScreen: false }"]
10
12
 
11
13
  delete-icon:
12
14
  desc: Ícone do botão de deletar.
@@ -23,19 +25,13 @@ props:
23
25
  default: {}
24
26
  type: Object
25
27
 
26
- icon:
27
- desc: Ícone do botão.
28
- default: sym_r_more_vert
29
- type: String
30
- examples: [start, end, between, around, center]
31
-
32
28
  list:
33
29
  desc: Lista de items que vão ser criados dentro do menu de ações.
34
30
  default: '{}'
35
31
  type: Object
36
32
  examples: [
37
33
  "{
38
- delete: {
34
+ visibility: {
39
35
  icon: 'sym_r_visibility',
40
36
  label: 'Visualizar',
41
37
  handler: () => alert('handler ativado')
@@ -43,10 +39,11 @@ props:
43
39
  }"
44
40
  ]
45
41
 
46
- use-label-on-small-screen:
47
- desc: Esconde o rótulo (label) do botão quando o tamanho da tela for pequeno (esta propriedade funciona se o "rotulo") for passado via propriedade "label".
48
- default: true
49
- type: Boolean
42
+ split-name:
43
+ desc: propriedade para definir o useSplit, habilita 2 ações diferentes no mesmo componente, separando o mesmo por uma barra, o valor deve ser alguma chave (key) existente na propriedade "list".
44
+ default: ''
45
+ type: String
46
+ examples: [visibility]
50
47
 
51
48
  use-label:
52
49
  desc: Habilita ou não o label no botão.
@@ -21,6 +21,16 @@ export default {
21
21
  validator: value => ['grey-9', 'primary', 'white'].includes(value)
22
22
  },
23
23
 
24
+ icon: {
25
+ type: String,
26
+ default: undefined
27
+ },
28
+
29
+ iconRight: {
30
+ type: String,
31
+ default: undefined
32
+ },
33
+
24
34
  useLabelOnSmallScreen: {
25
35
  default: true,
26
36
  type: Boolean
@@ -76,12 +86,17 @@ export default {
76
86
  ...(this.showLabel && { label: this.label }),
77
87
 
78
88
  ...attributes,
89
+ icon: this.icon,
90
+ iconRight: this.iconRight,
79
91
  class: [this.classes, externalClass]
80
92
  }
81
93
  },
82
94
 
83
95
  hasIconOnly () {
84
- return (!this.label || !this.showLabel) && (this.$attrs.icon || this.$attrs.iconRight)
96
+ return (
97
+ (!this.label || !this.showLabel) &&
98
+ ((this.icon && !this.iconRight) || (this.iconRight && !this.icon))
99
+ )
85
100
  },
86
101
 
87
102
  classes () {
@@ -13,6 +13,14 @@ props:
13
13
  type: String
14
14
  examples: ['grey-9', 'primary', 'white']
15
15
 
16
+ icon:
17
+ desc: Ícone a esquerda.
18
+ type: String
19
+
20
+ icon-right:
21
+ desc: Ícone a direita.
22
+ type: String
23
+
16
24
  use-hover-on-white-color:
17
25
  desc: Quando usamos a cor branca para contrast, em alguns casos podemos não querer ter um hover de outra cor justamente por conta do contraste, esta propriedade só funciona com variante "tertiary" e cor "white", para remover o hover sete o valor para "false".
18
26
  default: true
@@ -0,0 +1,120 @@
1
+ <template>
2
+ <div class="flex inline items-center qas-btn-dropdown">
3
+ <div v-if="hasLeftButton" :class="leftSideClasses">
4
+ <slot name="left-button">
5
+ <qas-btn variant="tertiary" v-bind="defaultButtonProps" @click="$emit('click', $event)">
6
+ <q-menu v-if="hasMenuOnLeftSide" anchor="bottom right" auto-close self="top right">
7
+ <div :class="menuContentClasses">
8
+ <slot />
9
+ </div>
10
+ </q-menu>
11
+ </qas-btn>
12
+ </slot>
13
+ </div>
14
+
15
+ <q-separator v-if="hasSeparator" class="q-mr-sm qas-btn-dropdown__separator self-center" dark vertical />
16
+
17
+ <div v-if="useSplit">
18
+ <qas-btn color="grey-9" :icon="dropdownIcon" variant="tertiary">
19
+ <q-menu v-if="hasDefaultSlot" anchor="bottom right" auto-close self="top right">
20
+ <div :class="menuContentClasses">
21
+ <slot />
22
+ </div>
23
+ </q-menu>
24
+ </qas-btn>
25
+ </div>
26
+ </div>
27
+ </template>
28
+
29
+ <script>
30
+ export default {
31
+ name: 'QasBtnDropdown',
32
+
33
+ inheritAttrs: false,
34
+
35
+ props: {
36
+ buttonProps: {
37
+ default: () => ({}),
38
+ type: Object
39
+ },
40
+
41
+ dropdownIcon: {
42
+ default: 'sym_r_more_vert',
43
+ type: String
44
+ },
45
+
46
+ useSplit: {
47
+ type: Boolean
48
+ },
49
+
50
+ useMenuPadding: {
51
+ type: Boolean
52
+ }
53
+ },
54
+
55
+ emits: ['click'],
56
+
57
+ computed: {
58
+ leftSideClasses () {
59
+ return {
60
+ 'q-mr-sm': this.useSplit
61
+ }
62
+ },
63
+
64
+ menuContentClasses () {
65
+ return {
66
+ 'q-pa-md': this.useMenuPadding
67
+ }
68
+ },
69
+
70
+ hasDefaultSlot () {
71
+ return !!this.$slots.default
72
+ },
73
+
74
+ hasMenuOnLeftSide () {
75
+ return this.hasDefaultSlot && !this.useSplit
76
+ },
77
+
78
+ defaultButtonProps () {
79
+ const {
80
+ icon,
81
+ iconRight,
82
+ color,
83
+ ...defaultProps
84
+ } = this.buttonProps
85
+
86
+ const defaultIconRight = iconRight || this.dropdownIcon
87
+
88
+ return {
89
+ useLabelOnSmallScreen: false,
90
+
91
+ ...defaultProps,
92
+
93
+ color: color || (!this.useSplit ? 'grey-9' : 'primary'),
94
+ ...(!this.useSplit && { iconRight: defaultIconRight }),
95
+ ...(this.useSplit && { icon })
96
+ }
97
+ },
98
+
99
+ isSmall () {
100
+ return this.$qas.screen.isSmall
101
+ },
102
+
103
+ hasLeftButton () {
104
+ return !this.isSmall || !this.useSplit
105
+ },
106
+
107
+ hasSeparator () {
108
+ return !this.isSmall && this.useSplit
109
+ }
110
+ }
111
+ }
112
+ </script>
113
+
114
+ <style lang="scss">
115
+ .qas-btn-dropdown {
116
+ &__separator {
117
+ height: 18px;
118
+ }
119
+ }
120
+ </style>
@@ -0,0 +1,42 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Componente semelhante ao QBtnDropdown porém utilizando o QasBtn e QSeparator para aplicar estilos padrões do Design System.
5
+
6
+ props:
7
+ button-props:
8
+ desc: Propriedades repassadas para o QasBtn.
9
+ default: {}
10
+ type: Object
11
+ examples: ["{ color: 'white', icon: 'sym_r_person' }"]
12
+
13
+ dropdown-icon:
14
+ desc: Ícone a direita do dropdown.
15
+ default: sym_r_more_vert
16
+ type: String
17
+
18
+ use-split:
19
+ desc: Divide o componente em 2 ações, o primeiro (direita) é um botão padrão, o segundo (esquerda) é um botão com ícone que abre o dropdown, criando uma separação com o QSeparator.
20
+ default: false
21
+ type: Boolean
22
+
23
+ use-menu-padding:
24
+ desc: Habilita o espaçamento dentro do menu de 16px (q-pa-md).
25
+ default: false
26
+ type: Boolean
27
+
28
+ slots:
29
+ default:
30
+ desc: Slot para passar o conteúdo do dropdown (menu)
31
+
32
+ left-button:
33
+ desc: Slot para substituir o botão a esquerda.
34
+
35
+ events:
36
+ 'click':
37
+ desc: Caso não tenha "useSplit", o evento é disparado ao clicar no componente como um todo, caso tenha "useSplit", o evento é disparado ao clicar no botão a esquerda.
38
+ params:
39
+ event:
40
+ desc: Evento nativo de click.
41
+ type: Object
42
+
@@ -1,28 +1,19 @@
1
1
  <template>
2
- <component v-bind="attributes" :is="tag" @click.stop="openConfirmDialog">
2
+ <component v-bind="attributes" :is="tag" @click.stop="onDelete">
3
3
  <template v-for="(_, name) in $slots" #[name]="context">
4
4
  <slot :name="name" v-bind="context || {}" />
5
5
  </template>
6
6
  </component>
7
-
8
- <qas-dialog v-model="showDialog" v-bind="defaultDialogProps" />
9
7
  </template>
10
8
 
11
9
  <script>
12
- import { Loading } from 'quasar'
13
- import { getAction } from '@bildvitta/store-adapter'
14
- import { NotifyError, NotifySuccess } from '../../plugins'
15
- import { useHistory } from '../../composables'
16
-
17
10
  import QasBtn from '../btn/QasBtn.vue'
18
- import QasDialog from '../dialog/QasDialog.vue'
19
11
 
20
12
  export default {
21
13
  name: 'QasDelete',
22
14
 
23
15
  components: {
24
- QasBtn,
25
- QasDialog
16
+ QasBtn
26
17
  },
27
18
 
28
19
  inheritAttrs: false,
@@ -69,12 +60,6 @@ export default {
69
60
  'update:deleting'
70
61
  ],
71
62
 
72
- data () {
73
- return {
74
- showDialog: false
75
- }
76
- },
77
-
78
63
  computed: {
79
64
  attributes () {
80
65
  return {
@@ -83,106 +68,35 @@ export default {
83
68
  }
84
69
  },
85
70
 
86
- defaultDialogProps () {
87
- return {
88
- card: {
89
- description: 'Tem certeza que deseja excluir este item?'
90
- },
91
-
92
- ok: {
93
- label: 'Excluir',
94
- onClick: this.destroy
95
- },
96
-
97
- ...this.dialogProps
98
- }
99
- },
100
-
101
71
  id () {
102
72
  return this.customId || this.$route.params.id
103
73
  },
104
74
 
105
- defaultNotifyMessages () {
106
- return {
107
- error: 'Não conseguimos excluir as informações. Por favor, tente novamente em alguns minutos.',
108
- success: 'Informações excluídas com sucesso.'
109
- }
110
- },
111
-
112
75
  isButton () {
113
76
  return this.tag === 'qas-btn'
114
77
  }
115
78
  },
116
79
 
117
80
  methods: {
118
- openConfirmDialog () {
119
- this.showDialog = true
120
- },
81
+ onDelete () {
82
+ this.$qas.delete({
83
+ deleteActionParams: {
84
+ entity: this.entity,
85
+ id: this.id,
86
+ url: this.url
87
+ },
121
88
 
122
- async destroy () {
123
- Loading.show()
124
- this.$emit('update:deleting', true)
89
+ dialogProps: this.dialogProps,
125
90
 
126
- try {
127
- const payload = { id: this.id, url: this.url }
91
+ useAutoDeleteRoute: this.useAutoDeleteRoute,
128
92
 
129
- this.$qas.logger.group(
130
- `QasDelete - destroy -> Payload do parâmetro do ${this.entity}/destroy`, [payload]
131
- )
93
+ // callbacks
94
+ onDelete: isDeleting => this.$emit('update:deleting', isDeleting),
132
95
 
133
- await getAction.call(this, {
134
- entity: this.entity,
135
- key: 'destroy',
136
- payload
137
- })
138
-
139
- NotifySuccess(this.defaultNotifyMessages.success)
140
-
141
- if (this.useAutoDeleteRoute) {
142
- const { destroyRoutes, history } = useHistory()
143
-
144
- // remove todas rotas que possuem o id do item excluído.
145
- const routesToBeDeleted = this.getRoutesToBeDeletedById(history.list)
146
- destroyRoutes(routesToBeDeleted)
147
- }
148
-
149
- // cria um evento para notificar que o item foi excluído no "window".
150
- this.createDeleteSuccessEvent()
151
-
152
- this.$emit('success')
153
-
154
- this.$qas.logger.info('QasDelete - destroy -> item deletado com sucesso!')
155
- } catch (error) {
156
- NotifyError(this.defaultNotifyMessages.error)
157
- this.$emit('error', error)
158
-
159
- this.$qas.logger.group(
160
- `QasDelete - destroy -> exceção da action ${this.entity}/destroy`,
161
- [error],
162
- { error: true }
163
- )
164
- } finally {
165
- Loading.hide()
166
- this.$emit('update:deleting', false)
167
- }
168
- },
169
-
170
- getRoutesToBeDeletedById (routes = []) {
171
- return routes.filter(({ params }) => params.id === this.id)
172
- },
96
+ onDeleteError: error => this.$emit('error', error),
173
97
 
174
- createDeleteSuccessEvent () {
175
- const event = new CustomEvent('delete-success', {
176
- bubbles: false,
177
- cancelable: false,
178
- detail: {
179
- entity: this.entity,
180
- url: this.url,
181
- id: this.id
182
- }
98
+ onDeleteSuccess: response => this.$emit('success', response)
183
99
  })
184
-
185
- window.dispatchEvent(event)
186
100
  }
187
101
  }
188
102
  }
@@ -21,7 +21,7 @@
21
21
 
22
22
  <footer v-if="!isInfoDialog">
23
23
  <slot name="actions">
24
- <qas-actions v-bind="actionsProps" :use-equal-width="hasAllActions" :use-full-width="hasSingleAction">
24
+ <qas-actions v-bind="formattedActionsProps">
25
25
  <template v-if="hasOk" #primary>
26
26
  <qas-btn v-close-popup="!useForm" class="full-width" variant="primary" v-bind="defaultOk" @click="submitHandler" />
27
27
  </template>
@@ -172,6 +172,20 @@ export default {
172
172
 
173
173
  isInfoDialog () {
174
174
  return !this.hasOk && !this.hasCancel
175
+ },
176
+
177
+ formattedActionsProps () {
178
+ const { useFullWidth, useEqualWidth } = this.actionsProps
179
+
180
+ if (useFullWidth || useEqualWidth) {
181
+ return this.actionsProps
182
+ }
183
+
184
+ return {
185
+ useFullWidth: this.hasSingleAction,
186
+ useEqualWidth: this.hasAllActions,
187
+ ...this.actionsProps
188
+ }
175
189
  }
176
190
  },
177
191
 
@@ -11,7 +11,7 @@
11
11
  <div>
12
12
  <div class="flex items-center justify-between q-py-md">
13
13
  <qas-label v-if="!useSingleLabel" :label="getRowLabel(index)" />
14
- <qas-actions-menu v-if="hasBlockActions(row)" :list="getActionsList(index, row)" :use-label-on-small-screen="false" />
14
+ <qas-actions-menu v-if="hasBlockActions(row)" v-bind="actionsMenuProps" :list="getActionsList(index, row)" />
15
15
  </div>
16
16
 
17
17
  <div ref="formGenerator" class="col-12 justify-between q-col-gutter-x-md row">
@@ -24,7 +24,7 @@
24
24
  </slot>
25
25
 
26
26
  <div v-if="hasInlineActions(row)" class="flex items-center qas-nested-fields__actions">
27
- <qas-actions-menu :list="getActionsList(index, row)" :use-label-on-small-screen="false" />
27
+ <qas-actions-menu v-bind="actionsMenuProps" :list="getActionsList(index, row)" />
28
28
  </div>
29
29
  </div>
30
30
 
@@ -55,7 +55,6 @@ export default {
55
55
  },
56
56
 
57
57
  useNegative: {
58
- default: true,
59
58
  type: Boolean
60
59
  },
61
60
 
@@ -35,7 +35,7 @@ props:
35
35
 
36
36
  use-negative:
37
37
  desc: Controla se pode ou não números negativos.
38
- default: true
38
+ default: false
39
39
  type: Boolean
40
40
 
41
41
  use-positive:
@@ -1,11 +1,17 @@
1
1
  <template>
2
2
  <qas-box class="q-px-lg q-py-md">
3
3
  <q-table ref="table" class="bg-white qas-table-generator text-grey-8" :class="tableClass" v-bind="attributes">
4
- <template v-for="(_, name) in $slots" #[name]="context">
5
- <slot v-if="hasBodySlot" name="body" :props="context" />
4
+ <template v-for="(_, name) in slots" #[name]="context">
5
+ <slot :name="name" v-bind="context" />
6
+ </template>
6
7
 
7
- <q-td v-else :key="name">
8
- <slot :name="name" v-bind="context || {}" />
8
+ <template v-for="(fieldName, index) in bodyCellNameSlots" :key="index" #[`body-cell-${fieldName}`]="context">
9
+ <q-td>
10
+ <component :is="tdChildComponent" v-bind="getTdChildComponentProps(context.row)">
11
+ <slot :name="`body-cell-${fieldName}`" v-bind="context || {}">
12
+ {{ context.row?.[fieldName] }}
13
+ </slot>
14
+ </component>
9
15
  </q-td>
10
16
  </template>
11
17
  </q-table>
@@ -26,6 +32,11 @@ export default {
26
32
  type: Array
27
33
  },
28
34
 
35
+ rowRouteFn: {
36
+ type: Function,
37
+ default: undefined
38
+ },
39
+
29
40
  fields: {
30
41
  default: () => ({}),
31
42
  type: [Array, Object]
@@ -50,6 +61,10 @@ export default {
50
61
  useScrollOnGrab: {
51
62
  type: Boolean,
52
63
  default: true
64
+ },
65
+
66
+ useExternalLink: {
67
+ type: Boolean
53
68
  }
54
69
  },
55
70
 
@@ -63,6 +78,36 @@ export default {
63
78
  },
64
79
 
65
80
  computed: {
81
+ tdChildComponent () {
82
+ if (this.useExternalLink) return 'a'
83
+
84
+ return this.rowRouteFn ? 'router-link' : 'span'
85
+ },
86
+
87
+ bodyCellNameSlots () {
88
+ if (this.hasBodyCellSlot) return []
89
+
90
+ return this.columns.length
91
+ ? this.columns.map(column => typeof column === 'object' ? column.name : column)
92
+ : Object.keys(this.fields)
93
+ },
94
+
95
+ slots () {
96
+ const slots = {}
97
+
98
+ for (const slotKey in this.$slots) {
99
+ if (slotKey.includes('body-cell-')) continue
100
+
101
+ slots[slotKey] = this.$slots[slotKey]
102
+ }
103
+
104
+ return slots
105
+ },
106
+
107
+ hasBodyCellSlot () {
108
+ return !!this.$slots['body-cell']
109
+ },
110
+
66
111
  attributes () {
67
112
  const attributes = {
68
113
  columns: this.columnsByFields,
@@ -123,14 +168,6 @@ export default {
123
168
  return columns
124
169
  },
125
170
 
126
- hasBodySlot () {
127
- return !!this.$slots.body
128
- },
129
-
130
- hasTbodySlot () {
131
- return !!this.$slots.tbody
132
- },
133
-
134
171
  hasFields () {
135
172
  return Object.keys(this.fields).length
136
173
  },
@@ -164,6 +201,10 @@ export default {
164
201
 
165
202
  hasScrollOnGrab () {
166
203
  return !!Object.keys(this.scrollOnGrab).length
204
+ },
205
+
206
+ isScrolling () {
207
+ return this.hasScrollOnGrab && this.scrollOnGrab.haveMoved()
167
208
  }
168
209
  },
169
210
 
@@ -223,11 +264,6 @@ export default {
223
264
  }
224
265
  },
225
266
 
226
- onRowClick () {
227
- if (this.hasScrollOnGrab && this.scrollOnGrab.haveMoved()) return
228
- this.$attrs.onRowClick(...arguments)
229
- },
230
-
231
267
  setObserver () {
232
268
  this.elementToObserve = this.getTableElement()
233
269
  this.resizeObserver = new ResizeObserver(entries => {
@@ -239,6 +275,27 @@ export default {
239
275
 
240
276
  destroyObserver () {
241
277
  this.resizeObserver.unobserve(this.elementToObserve)
278
+ },
279
+
280
+ getTdChildComponentProps (row) {
281
+ if (!this.rowRouteFn) return
282
+
283
+ return {
284
+ class: 'text-no-decoration text-grey-8 flex full-width items-center full-height',
285
+ [this.useExternalLink ? 'href' : 'to']: this.rowRouteFn(row),
286
+ onClick: this.onRowClickHandler,
287
+ ...(this.useExternalLink && { target: '_blank' })
288
+ }
289
+ },
290
+
291
+ onRowClickHandler (event) {
292
+ if (this.isScrolling) return event.preventDefault()
293
+ },
294
+
295
+ onRowClick () {
296
+ if (this.isScrolling) return
297
+
298
+ this.$attrs.onRowClick(...arguments)
242
299
  }
243
300
  }
244
301
  }
@@ -36,6 +36,16 @@ props:
36
36
  default: true
37
37
  type: Boolean
38
38
 
39
+ row-route-fn:
40
+ desc: Usado quando há a necessidade de alteração de rota ao clicar em um item da tabela(a linha passa ser um <a> habilitando a opção de abrir em uma nova aba).
41
+ type: Function
42
+ examples: ["() => ({ name: 'UsersList' })"]
43
+
44
+ use-external-link:
45
+ desc: Usado em conjunto com a prop "row-route-fn" quando há a necessidade da rota ser um link externo.
46
+ default: false
47
+ type: Boolean
48
+
39
49
  slots:
40
50
  body:
41
51
  desc: Acesso direto dentro do `tbody`.
@@ -1,6 +1,7 @@
1
1
  .qas-btn {
2
2
  $root: &;
3
3
 
4
+ box-shadow: none;
4
5
  min-height: 48px !important;
5
6
  padding: var(--qas-spacing-xs) var(--qas-spacing-lg) !important;
6
7
  text-transform: initial !important;
@@ -1,4 +1,5 @@
1
1
  @import './button';
2
+ @import './separator';
2
3
  @import './shadow';
3
4
  @import './spacing';
4
5
  @import './typography';
@@ -0,0 +1,2 @@
1
+ $separator-color : $grey-4;
2
+ $separator-dark-color : $grey-6;
@@ -0,0 +1,89 @@
1
+ import { Dialog, NotifySuccess, NotifyError } from 'asteroid'
2
+ import { Loading } from 'quasar'
3
+ import { getAction } from '@bildvitta/store-adapter'
4
+ import { useHistory } from '../../composables'
5
+
6
+ export default function (config = {}) {
7
+ const {
8
+ dialogProps = {},
9
+ deleteActionParams = {},
10
+ useAutoDeleteRoute,
11
+
12
+ // callbacks
13
+ onDelete = () => {},
14
+ onDeleteError = () => {},
15
+ onDeleteSuccess = () => {},
16
+ deleteAction
17
+ } = config
18
+
19
+ const { entity, id, url } = deleteActionParams
20
+
21
+ const defaultDialogProps = {
22
+ card: { description: 'Tem certeza que deseja excluir este item?' },
23
+
24
+ ok: { label: 'Excluir', onClick: () => destroy.call(this) },
25
+
26
+ ...dialogProps
27
+ }
28
+
29
+ const defaultNotifyMessages = {
30
+ error: 'Não conseguimos excluir as informações. Por favor, tente novamente em alguns minutos.',
31
+ success: 'Informações excluídas com sucesso.'
32
+ }
33
+
34
+ async function destroy () {
35
+ Loading.show()
36
+
37
+ try {
38
+ onDelete(true)
39
+
40
+ const hasDeleteAction = typeof deleteAction === 'function'
41
+
42
+ const payload = { id, url }
43
+ const destroyActionParams = { entity, key: 'destroy', payload }
44
+
45
+ const response = hasDeleteAction
46
+ ? await deleteAction(payload)
47
+ : await getAction.call(this, destroyActionParams)
48
+
49
+ NotifySuccess(defaultNotifyMessages.success)
50
+
51
+ if (useAutoDeleteRoute) {
52
+ const { destroyRoutes, history } = useHistory()
53
+
54
+ // remove todas rotas que possuem o id do item excluído.
55
+ const routesToBeDeleted = getRoutesToBeDeletedById(history.list)
56
+ destroyRoutes(routesToBeDeleted)
57
+ }
58
+
59
+ // cria um evento para notificar que o item foi excluído no "window".
60
+ createDeleteSuccessEvent()
61
+
62
+ onDeleteSuccess(response)
63
+ } catch (error) {
64
+ onDeleteError(error)
65
+
66
+ NotifyError(defaultNotifyMessages.error)
67
+ } finally {
68
+ onDelete(false)
69
+
70
+ Loading.hide()
71
+ }
72
+ }
73
+
74
+ function getRoutesToBeDeletedById (routes = []) {
75
+ return routes.filter(({ params }) => params.id === id)
76
+ }
77
+
78
+ function createDeleteSuccessEvent () {
79
+ const event = new CustomEvent('delete-success', {
80
+ bubbles: false,
81
+ cancelable: false,
82
+ detail: deleteActionParams
83
+ })
84
+
85
+ window.dispatchEvent(event)
86
+ }
87
+
88
+ Dialog(defaultDialogProps)
89
+ }
@@ -0,0 +1,71 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Plugin que implementa a action `destroy` do `StoreModule` adicionando comportamento de confirmação antes de excluir, este mesmo plugin é utilizado no componente `QasDelete`.
5
+
6
+ inject:
7
+ 'this.$qas.delete(config)':
8
+ params:
9
+ config:
10
+ type: Object
11
+ desc: Objeto de configuração do plugin.
12
+ params:
13
+ dialogProps:
14
+ type: Object
15
+ desc: Propriedades repassadas para o "QasDialog".
16
+
17
+ deleteActionParams:
18
+ type: Object
19
+ desc: Propriedades de configuração da action destroy.
20
+ params:
21
+ entity:
22
+ type: String
23
+ desc: entidade da store.
24
+ default: ''
25
+
26
+ id:
27
+ type: String
28
+ desc: Identificador (id, uuid, slug) do item a ser deletado.
29
+ default: ''
30
+
31
+ url:
32
+ type: String
33
+ desc: URL do endpoint do item a ser deletado.
34
+ default: ''
35
+
36
+ 'onDelete -> function (isDeleting)':
37
+ type: Function
38
+ desc: Este callback é chamado quando inicializa a ação de deleção e quando finaliza, independente se der erro ou sucesso.
39
+ params:
40
+ isDeleting:
41
+ type: Boolean
42
+ desc: Retorna "true" caso esteja ocorrendo a ação de deleção e "false" quando finalizada.
43
+ default: false
44
+
45
+ 'onDeleteError -> function (error)':
46
+ type: Function
47
+ desc: Este callback é chamado quando a ação de deleção é finalizada com erro.
48
+ params:
49
+ error:
50
+ type: Object
51
+ desc: Objeto de retorno de erro
52
+ default: {}
53
+
54
+ 'onDeleteSuccess -> function (response)':
55
+ type: Function
56
+ desc: Este callback é chamado quando a ação de deleção é finalizada com sucesso.
57
+ params:
58
+ error:
59
+ type: Object
60
+ desc: Objeto de resposta da API
61
+ default: {}
62
+
63
+ 'deleteAction -> function (deleteActionParams) || undefined':
64
+ type: [Function, undefined]
65
+ desc: Quando utilizado este plugin fora de componentes vue, não temos acesso ao contexto "this", neste caso é necessário passar a action "destroy" que deseja executar, é neste momento que o callback é chamado / utilizado, caso esteja dentro do componente vue, não repasse esse callback.
66
+ params:
67
+ error:
68
+ type: Object
69
+ desc: Objeto deleteActionParams enviado anteriormente como configuração do plugin Delete.
70
+ default: {}
71
+ examples: ["{ ..., deleteAction: params => useUsers.destroy(params) }"]
@@ -1,7 +1,7 @@
1
1
  type: component
2
2
 
3
3
  meta:
4
- desc: Plugin que implementa o "QNotify" do quasar para notificações de sucesso.
4
+ desc: Plugin que implementa o `QasDialog`.
5
5
 
6
6
  inject:
7
7
  'this.$qas.dialog(config)':
@@ -1,3 +1,4 @@
1
+ import Delete from './delete/Delete.js'
1
2
  import Dialog from './dialog/Dialog.js'
2
3
  import Logger from './logger/Logger.js'
3
4
  import NotifyError from './notify-error/NotifyError.js'
@@ -5,6 +6,7 @@ import NotifySuccess from './notify-success/NotifySuccess.js'
5
6
  import Screen from './screen/Screen.js'
6
7
 
7
8
  export {
9
+ Delete,
8
10
  Dialog,
9
11
  Logger,
10
12
  NotifyError,
package/src/vue-plugin.js CHANGED
@@ -9,6 +9,7 @@ import QasBadge from './components/badge/QasBadge.vue'
9
9
  import QasBox from './components/box/QasBox.vue'
10
10
  import QasBreakline from './components/breakline/QasBreakline.vue'
11
11
  import QasBtn from './components/btn/QasBtn.vue'
12
+ import QasBtnDropdown from './components/btn-dropdown/QasBtnDropdown.vue'
12
13
  import QasCard from './components/card/QasCard.vue'
13
14
  import QasCheckboxGroup from './components/checkbox-group/QasCheckboxGroup.vue'
14
15
  import QasCopy from './components/copy/QasCopy.vue'
@@ -60,6 +61,7 @@ import { Notify, Loading, Quasar, Dialog as QuasarDialog } from 'quasar'
60
61
 
61
62
  // Plugins
62
63
  import {
64
+ Delete,
63
65
  Dialog,
64
66
  NotifyError,
65
67
  NotifySuccess,
@@ -86,6 +88,7 @@ function install (app) {
86
88
  app.component('QasBox', QasBox)
87
89
  app.component('QasBreakline', QasBreakline)
88
90
  app.component('QasBtn', QasBtn)
91
+ app.component('QasBtnDropdown', QasBtnDropdown)
89
92
  app.component('QasCard', QasCard)
90
93
  app.component('QasCheckboxGroup', QasCheckboxGroup)
91
94
  app.component('QasCopy', QasCopy)
@@ -139,6 +142,7 @@ function install (app) {
139
142
  }
140
143
 
141
144
  app.config.globalProperties.$qas = {
145
+ delete: params => Delete.call(app.config.globalProperties, params),
142
146
  dialog: Dialog,
143
147
  error: NotifyError,
144
148
  logger: Logger(),
@@ -164,6 +168,7 @@ export {
164
168
  QasBox,
165
169
  QasBreakline,
166
170
  QasBtn,
171
+ QasBtnDropdown,
167
172
  QasCard,
168
173
  QasCheckboxGroup,
169
174
  QasCopy,