@bildvitta/quasar-ui-asteroid 3.4.0 → 3.5.0-beta.1

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.
Files changed (37) hide show
  1. package/package.json +2 -2
  2. package/src/components/actions-menu/QasActionsMenu.vue +86 -37
  3. package/src/components/actions-menu/QasActionsMenu.yml +6 -6
  4. package/src/components/app-menu/QasAppMenu.vue +1 -1
  5. package/src/components/copy/QasCopy.vue +1 -1
  6. package/src/components/delete/QasDelete.vue +1 -1
  7. package/src/components/field/QasField.vue +14 -6
  8. package/src/components/filters/QasFilters.vue +6 -1
  9. package/src/components/filters/QasFilters.yml +6 -0
  10. package/src/components/form-generator/QasFormGenerator.yml +1 -1
  11. package/src/components/grid-generator/QasGridGenerator.yml +1 -1
  12. package/src/components/list-view/QasListView.vue +67 -8
  13. package/src/components/list-view/QasListView.yml +4 -0
  14. package/src/components/nested-fields/QasNestedFields.vue +101 -41
  15. package/src/components/nested-fields/QasNestedFields.yml +30 -10
  16. package/src/components/option-group/QasOptionGroup.vue +54 -0
  17. package/src/components/option-group/QasOptionGroup.yml +30 -0
  18. package/src/components/pagination/QasPagination.vue +27 -0
  19. package/src/components/pagination/QasPagination.yml +4 -0
  20. package/src/components/select/QasSelect.vue +15 -8
  21. package/src/components/select/QasSelect.yml +0 -4
  22. package/src/components/table-generator/QasTableGenerator.vue +10 -2
  23. package/src/components/table-generator/QasTableGenerator.yml +14 -0
  24. package/src/components/text-truncate/QasTextTruncate.vue +1 -1
  25. package/src/css/components/item.scss +4 -0
  26. package/src/css/plugins/index.scss +1 -0
  27. package/src/css/plugins/notify.scss +40 -0
  28. package/src/css/variables/button.scss +2 -2
  29. package/src/css/variables/spacing.scss +5 -5
  30. package/src/css/variables/typography.scss +24 -24
  31. package/src/helpers/set-scroll-on-grab.js +13 -4
  32. package/src/index.scss +4 -1
  33. package/src/mixins/generator.js +1 -1
  34. package/src/plugins/notify-error/NotifyError.js +7 -5
  35. package/src/plugins/notify-success/NotifySuccess.js +7 -5
  36. package/src/shared/notify-config.js +7 -0
  37. package/src/vue-plugin.js +6 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bildvitta/quasar-ui-asteroid",
3
3
  "description": "Asteroid",
4
- "version": "3.4.0",
4
+ "version": "3.5.0-beta.1",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "dist/asteroid.cjs.min.js",
@@ -64,4 +64,4 @@
64
64
  "tags": "dist/vetur/asteroid-tags.json",
65
65
  "attributes": "dist/vetur/asteroid-attributes.json"
66
66
  }
67
- }
67
+ }
@@ -1,49 +1,45 @@
1
1
  <template>
2
- <qas-btn class="qas-actions-menu" color="primary" :icon="icon" :label="label" outline padding="md" :use-label-on-small-screen="false">
3
- <q-menu class="qas-actions-menu__menu">
4
- <q-list class="qas-actions-menu__list" separator>
5
- <slot v-for="(item, key) in list" :item="item" :name="key">
6
- <q-item :key="key" class="text-primary" clickable v-bind="item.props" @click="onClick(item)">
7
- <q-item-section>
8
- <div class="flex items-center justify-center q-gutter-x-md">
9
- <q-icon :name="item.icon" size="sm" />
2
+ <div v-if="hasActions">
3
+ <component :is="component.is" flat v-bind="component.props" :use-label-on-small-screen="useLabelOnSmallScreen" @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>
10
13
  <div>{{ item.label }}</div>
11
- </div>
12
- </q-item-section>
13
- </q-item>
14
- </slot>
15
-
16
- <qas-delete v-if="hasDelete" v-bind="deleteProps" class="text-negative" clickable tag="q-item">
17
- <q-item-section>
18
- <div class="flex items-center justify-center q-gutter-x-sm">
19
- <q-icon :name="deleteIcon" size="sm" />
20
- <div>{{ deleteLabel }}</div>
21
- </div>
22
- </q-item-section>
23
- </qas-delete>
24
- </q-list>
25
- </q-menu>
26
- </qas-btn>
14
+ </q-item-section>
15
+ </component>
16
+ </slot>
17
+ </q-list>
18
+ </q-menu>
19
+ </component>
20
+ </div>
27
21
  </template>
28
22
 
29
23
  <script>
30
24
  import QasBtn from '../btn/QasBtn.vue'
25
+ import QasDelete from '../delete/QasDelete.vue'
31
26
 
32
27
  export default {
33
28
  name: 'QasActionsMenu',
34
29
 
35
30
  components: {
36
- QasBtn
31
+ QasBtn,
32
+ QasDelete
37
33
  },
38
34
 
39
35
  props: {
40
36
  icon: {
41
- default: 'o_settings',
37
+ default: 'o_more_vert',
42
38
  type: String
43
39
  },
44
40
 
45
41
  label: {
46
- default: 'Configurações',
42
+ default: 'Opções',
47
43
  type: String
48
44
  },
49
45
 
@@ -65,17 +61,79 @@ export default {
65
61
  deleteProps: {
66
62
  default: () => ({}),
67
63
  type: Object
64
+ },
65
+
66
+ useLabelOnSmallScreen: {
67
+ default: true,
68
+ type: Boolean
68
69
  }
69
70
  },
70
71
 
71
72
  computed: {
73
+ actions () {
74
+ return {
75
+ ...this.list,
76
+ ...(this.hasDelete && {
77
+ delete: {
78
+ icon: this.deleteIcon,
79
+ label: this.deleteLabel,
80
+ props: {
81
+ ...this.deleteProps,
82
+ tag: this.hasMoreThanOneAction ? 'q-item' : 'qas-btn'
83
+ }
84
+ }
85
+ })
86
+ }
87
+ },
88
+
89
+ component () {
90
+ const props = {}
91
+
92
+ if (this.hasMoreThanOneAction) {
93
+ props.label = 'Opções'
94
+ props.iconRight = this.icon
95
+ props.textColor = 'dark'
96
+ } else {
97
+ props.icon = this.actions[this.firstItemKey]?.icon
98
+ props.label = this.actions[this.firstItemKey]?.label
99
+ props.color = 'primary'
100
+ }
101
+
102
+ this.hasDelete && Object.assign(props, this.deleteProps)
103
+
104
+ return {
105
+ is: this.hasMoreThanOneAction || !this.hasDelete ? 'qas-btn' : 'qas-delete',
106
+ props
107
+ }
108
+ },
109
+
110
+ firstItemKey () {
111
+ return Object.keys(this.actions)?.[0]
112
+ },
113
+
114
+ hasActions () {
115
+ return !!Object.keys(this.actions).length
116
+ },
117
+
72
118
  hasDelete () {
73
119
  return !!Object.keys(this.deleteProps).length
120
+ },
121
+
122
+ hasMoreThanOneAction () {
123
+ return Object.keys(this.actions || {}).length > 1
74
124
  }
75
125
  },
76
126
 
77
127
  methods: {
78
- onClick (item) {
128
+ getComponent (key) {
129
+ return key === 'delete' ? 'qas-delete' : 'q-item'
130
+ },
131
+
132
+ onClick (item = {}) {
133
+ if (!this.hasMoreThanOneAction) {
134
+ item = this.actions[this.firstItemKey]
135
+ }
136
+
79
137
  if (typeof item.handler === 'function') {
80
138
  const { handler, ...filtered } = item
81
139
  item.handler(filtered)
@@ -84,12 +142,3 @@ export default {
84
142
  }
85
143
  }
86
144
  </script>
87
-
88
- <style lang="scss">
89
- .qas-actions-menu {
90
- &__list {
91
- width: 265px;
92
- z-index: 1;
93
- }
94
- }
95
- </style>
@@ -21,15 +21,10 @@ props:
21
21
 
22
22
  icon:
23
23
  desc: Ícone do botão.
24
- default: o_settings
24
+ default: o_more_vert
25
25
  type: String
26
26
  examples: [start, end, between, around, center]
27
27
 
28
- label:
29
- desc: Rotulo do botão.
30
- default: Configurações
31
- type: String
32
-
33
28
  list:
34
29
  desc: Lista de items que vão ser criados dentro do menu de ações.
35
30
  default: '{}'
@@ -44,6 +39,11 @@ props:
44
39
  }"
45
40
  ]
46
41
 
42
+ use-label-on-small-screen:
43
+ desc: Esconde o rótulo (label) do botão quando o tamanho da tela for pequeno (esta propriedade só funciona se o "rotulo") for passado via propriedade "label".
44
+ default: true
45
+ type: Boolean
46
+
47
47
  slots:
48
48
  '[nome-da-chave]':
49
49
  desc: 'Slot dinâmico gerado a partir das chaves dentro do objeto da prop "list"'
@@ -179,7 +179,7 @@ export default {
179
179
  }
180
180
 
181
181
  &__children.q-item {
182
- padding-left: var(--qas-spacing-lg);
182
+ padding-left: var(--qas-spacing-xl);
183
183
  }
184
184
 
185
185
  &__item-children.q-item + &__item-children.q-item {
@@ -2,7 +2,7 @@
2
2
  <span>
3
3
  <slot>{{ text }}</slot>
4
4
 
5
- <qas-btn class="q-ml-xs" color="grey-7" flat :icon="icon" :loading="isLoading" round :size="size" @click="copy">
5
+ <qas-btn class="q-ml-xs" color="grey-7" flat :icon="icon" :loading="isLoading" round :size="size" @click.stop="copy">
6
6
  <q-tooltip>Copiar</q-tooltip>
7
7
  </qas-btn>
8
8
  </span>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <component v-bind="$attrs" :is="tag" @click="openConfirmDialog">
2
+ <component v-bind="$attrs" :is="tag" @click.stop="openConfirmDialog">
3
3
  <template v-for="(_, name) in $slots" #[name]="context">
4
4
  <slot :name="name" v-bind="context || {}" />
5
5
  </template>
@@ -11,9 +11,10 @@ import QasCheckboxGroup from '../checkbox-group/QasCheckboxGroup.vue'
11
11
  import QasDateTimeInput from '../date-time-input/QasDateTimeInput.vue'
12
12
  import QasInput from '../input/QasInput.vue'
13
13
  import QasNumericInput from '../numeric-input/QasNumericInput.vue'
14
+ import QasOptionGroup from '../option-group/QasOptionGroup.vue'
14
15
  import QasPasswordInput from '../password-input/QasPasswordInput.vue'
15
- import QasUploader from '../uploader/QasUploader.vue'
16
16
  import QasSignatureUploader from '../signature-uploader/QasSignatureUploader.vue'
17
+ import QasUploader from '../uploader/QasUploader.vue'
17
18
 
18
19
  const attributesProfile = {
19
20
  maxLength: 'maxlength',
@@ -29,9 +30,10 @@ export default {
29
30
  QasDateTimeInput,
30
31
  QasInput,
31
32
  QasNumericInput,
33
+ QasOptionGroup,
32
34
  QasPasswordInput,
33
- QasUploader,
34
- QasSignatureUploader
35
+ QasSignatureUploader,
36
+ QasUploader
35
37
  },
36
38
 
37
39
  inheritAttrs: false,
@@ -77,7 +79,6 @@ export default {
77
79
  maxFiles,
78
80
  useIso,
79
81
  useLazyLoading,
80
- useSearch,
81
82
  useStrengthChecker
82
83
  } = this.formattedField
83
84
 
@@ -138,14 +139,14 @@ export default {
138
139
 
139
140
  boolean: { is: 'q-toggle', label, ...error },
140
141
  checkbox: { is: 'qas-checkbox-group', label, options, ...error },
141
- radio: { is: 'q-option-group', label, options, type: 'radio', ...error },
142
+ radio: { is: 'qas-option-group', label, options },
142
143
 
143
144
  upload: { is: 'qas-uploader', accept, autoUpload: true, entity, label, multiple, readonly, maxFiles, ...error },
144
145
  editor: { is: 'q-editor', toolbar, ...error },
145
146
 
146
147
  'signature-uploader': { is: 'qas-signature-uploader', entity, uploadLabel: label, ...error },
147
148
 
148
- select: { is: 'qas-select', entity, name, multiple, options, useSearch, useLazyLoading, ...input }
149
+ select: { is: 'qas-select', entity, name, multiple, options, useLazyLoading, ...input }
149
150
  }
150
151
 
151
152
  return { ...(profiles[type] || profiles.default), ...this.$attrs }
@@ -158,11 +159,18 @@ export default {
158
159
  // This computed will change the key name when the server sends different key.
159
160
  formattedField () {
160
161
  const field = {}
162
+ const nonRequiredFieldsLabel = ['boolean', 'checkbox', 'radio']
161
163
 
162
164
  for (const key in this.field) {
163
165
  field[attributesProfile[key] || key] = this.field[key]
164
166
  }
165
167
 
168
+ const { label, required, type } = field
169
+
170
+ if (required && label && !nonRequiredFieldsLabel.includes(type)) {
171
+ field.label = `${label}*`
172
+ }
173
+
166
174
  return field
167
175
  },
168
176
 
@@ -27,7 +27,7 @@
27
27
 
28
28
  <q-form v-else class="q-gutter-y-md q-pa-md" @submit.prevent="filter()">
29
29
  <div v-for="(field, index) in fields" :key="index">
30
- <qas-field v-model="filters[field.name]" :data-cy="`filters-${field.name}-field`" dense :field="field" />
30
+ <qas-field v-model="filters[field.name]" :data-cy="`filters-${field.name}-field`" dense :field="field" v-bind="fieldsProps[field.name]" />
31
31
  </div>
32
32
 
33
33
  <div class="text-right">
@@ -78,6 +78,11 @@ export default {
78
78
  type: String
79
79
  },
80
80
 
81
+ fieldsProps: {
82
+ default: () => ({}),
83
+ type: Object
84
+ },
85
+
81
86
  useFilterButton: {
82
87
  default: true,
83
88
  type: Boolean
@@ -9,6 +9,12 @@ props:
9
9
  required: true
10
10
  type: String
11
11
 
12
+ fields-props:
13
+ desc: Propriedade para repassar propriedades para cada campo individualmente.
14
+ default: {}
15
+ type: Object
16
+ examples: ["{ name: { dense: true, onClick: () => alert('Estou sendo clicado') } }"]
17
+
12
18
  search-placeholder:
13
19
  desc: Placeholder do campo de busca.
14
20
  default: Pesquisar...
@@ -46,7 +46,7 @@ props:
46
46
 
47
47
  gutter:
48
48
  desc: Espaçamento entre colunas.
49
- default: md
49
+ default: lg
50
50
  type: [String, Boolean]
51
51
  examples: [xs, sm, md, lg, xl, false]
52
52
 
@@ -28,7 +28,7 @@ props:
28
28
 
29
29
  gutter:
30
30
  desc: Espaçamento entre colunas.
31
- default: md
31
+ default: lg
32
32
  type: String
33
33
  examples: [xs, sm, md, lg, xl]
34
34
 
@@ -16,9 +16,8 @@
16
16
 
17
17
  <div v-else-if="!mx_isFetching">
18
18
  <slot name="empty-results">
19
- <div class="q-my-xl text-center">
20
- <q-icon class="q-mb-sm text-center" color="grey-7" name="o_search" size="38px" />
21
- <div class="text-grey-7">Nenhum item encontrado.</div>
19
+ <div class="q-my-lg text-body1 text-grey-7">
20
+ Não itens para serem exibidos.
22
21
  </div>
23
22
  </slot>
24
23
  </div>
@@ -27,8 +26,8 @@
27
26
  <q-spinner color="grey" size="3em" />
28
27
  </div>
29
28
 
30
- <div v-if="hasPages" class="justify-center q-mt-lg">
31
- <q-pagination v-model="page" boundary-links class="flex-center" direction-links :input="$q.screen.lt.sm" :max="totalPages" :max-pages="6" @click="changePage" />
29
+ <div v-if="hasPages" class="flex items-center q-mt-sm" :class="paginationClasses">
30
+ <qas-pagination v-model="page" :max="totalPages" @click="changePage" />
32
31
  </div>
33
32
 
34
33
  <q-inner-loading :showing="hasResults && mx_isFetching">
@@ -44,12 +43,14 @@
44
43
  <script>
45
44
  import { viewMixin, contextMixin } from '../../mixins'
46
45
  import QasFilters from '../filters/QasFilters.vue'
46
+ import QasPagination from '../pagination/QasPagination.vue'
47
47
  import { extend } from 'quasar'
48
48
  import { getState, getAction } from '@bildvitta/store-adapter'
49
49
 
50
50
  export default {
51
51
  components: {
52
- QasFilters
52
+ QasFilters,
53
+ QasPagination
53
54
  },
54
55
 
55
56
  mixins: [contextMixin, viewMixin],
@@ -65,6 +66,10 @@ export default {
65
66
  type: Array
66
67
  },
67
68
 
69
+ useAutoHandleOnDelete: {
70
+ type: Boolean
71
+ },
72
+
68
73
  useRefresh: {
69
74
  default: true,
70
75
  type: Boolean
@@ -89,7 +94,8 @@ export default {
89
94
 
90
95
  data () {
91
96
  return {
92
- page: 1
97
+ page: 1,
98
+ resultsQuantity: 0
93
99
  }
94
100
  },
95
101
 
@@ -116,6 +122,10 @@ export default {
116
122
 
117
123
  showResults () {
118
124
  return this.hasResults || this.useResultsAreaOnly
125
+ },
126
+
127
+ paginationClasses () {
128
+ return this.$qas.screen.isSmall ? 'justify-center column' : 'justify-end'
119
129
  }
120
130
  },
121
131
 
@@ -142,6 +152,18 @@ export default {
142
152
  this.setCurrentPage()
143
153
  },
144
154
 
155
+ mounted () {
156
+ if (!this.useAutoHandleOnDelete) return
157
+
158
+ window.addEventListener('delete-success', this.onDeleteResult)
159
+ },
160
+
161
+ unmounted () {
162
+ if (!this.useAutoHandleOnDelete) return
163
+
164
+ window.removeEventListener('delete-success', this.onDeleteResult)
165
+ },
166
+
145
167
  methods: {
146
168
  changePage () {
147
169
  const query = { ...this.$route.query, page: this.page }
@@ -168,7 +190,8 @@ export default {
168
190
  payload
169
191
  })
170
192
 
171
- const { errors, fields, metadata } = response.data
193
+ const { errors, fields, metadata, results } = response.data
194
+ this.resultsQuantity = results.length
172
195
 
173
196
  this.mx_setErrors(errors)
174
197
  this.mx_setFields(fields)
@@ -210,6 +233,42 @@ export default {
210
233
 
211
234
  setCurrentPage () {
212
235
  this.page = parseInt(this.$route.query.page || 1)
236
+ },
237
+
238
+ onDeleteResult ({ detail: { entity } }) {
239
+ const { page } = this.mx_context
240
+
241
+ /*
242
+ * - se a entidade que estiver sendo excluída for diferente da entidade da listagem, ignora.
243
+ * - se a ultima pagina da paginação for igual a pagina atual e tiver mais de um resultado, ignora.
244
+ * - se não existir paginação (somente 1), ignora.
245
+ */
246
+ const skipRefreshList = [
247
+ (entity !== this.entity),
248
+ (this.totalPages === page && this.resultsQuantity > 1),
249
+ (this.totalPages === 1)
250
+ ]
251
+
252
+ this.resultsQuantity -= 1
253
+
254
+ if (skipRefreshList.find(Boolean)) return
255
+
256
+ /*
257
+ * caso eu remova o ultimo item da ultima pagina eu volto ele para a pagina anterior
258
+ * ex: estou na pagina 3 que é a ultima pagina, e removo o ultimo item dela, eu volto o usuário para pagina 2
259
+ */
260
+ if (this.resultsQuantity === 1 || !this.resultsQuantity) {
261
+ const { path, query } = this.$route
262
+
263
+ this.$router.replace({ path, query: { ...query, page: query.page - 1 } })
264
+ return
265
+ }
266
+
267
+ /*
268
+ * caso remova algo de uma pagina que não seja a ultima, chama o método fetchList novamente
269
+ * ex: estou na pagina 2 e existem 3 paginas, removo um item da pagina 2, então chamo o método fetchList
270
+ */
271
+ this.mx_fetchHandler({ ...this.mx_context, url: this.url }, this.fetchList)
213
272
  }
214
273
  }
215
274
  }
@@ -58,6 +58,10 @@ props:
58
58
  desc: Envia como parâmetro para a action "fetchList" do modulo correspondente a "entity".
59
59
  type: String
60
60
 
61
+ use-auto-handle-on-delete:
62
+ desc: Controla se o componente vai lidar automaticamente quando acontecer algum delete compatível com a listagem.
63
+ type: Boolean
64
+
61
65
  use-boundary:
62
66
  desc: Controla o limite que o FormView terá, quando é "false", a tag pai deixa de ser um "QPage" para ser uma "div" e é removido as classes "container" e "spaced", comumente utilizando quando precisa usar o QasFormView dentro de um dialog.
63
67
  default: true