@bildvitta/quasar-ui-asteroid 3.8.0-beta.1 → 3.8.0-beta.3

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.8.0-beta.1",
4
+ "version": "3.8.0-beta.3",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "dist/asteroid.cjs.min.js",
@@ -55,7 +55,7 @@ props:
55
55
  desc: propriedade para utilizar `100% do maxWidth`.
56
56
  type: Boolean
57
57
 
58
- use-validation-at-once:
58
+ use-validation-all-at-once:
59
59
  desc: Valida todos os campos de uma única vez, ao invés de ser um por vez (que é o padrão).
60
60
  type: Boolean
61
61
 
@@ -144,7 +144,7 @@ export default {
144
144
  upload: { is: 'qas-uploader', accept, autoUpload: true, entity, label, multiple, readonly, maxFiles, ...error },
145
145
  editor: { is: 'q-editor', toolbar, ...error },
146
146
 
147
- 'signature-uploader': { is: 'qas-signature-uploader', entity, uploadLabel: label, ...error },
147
+ 'signature-uploader': { is: 'qas-signature-uploader', uploaderProps: { entity, label, ...error } },
148
148
 
149
149
  select: { is: 'qas-select', entity, name, multiple, options, useLazyLoading, ...input }
150
150
  }
@@ -1,11 +1,11 @@
1
1
  <template>
2
- <section class="q-mb-xl qas-filters">
2
+ <section class="qas-filters" :class="filtersClass">
3
3
  <div v-if="showFilters" class="q-col-gutter-x-md row">
4
4
  <div v-if="showSearch" class="col-12 col-md-6">
5
5
  <slot :filter="filter" name="search">
6
6
  <q-form v-if="useSearch" @submit.prevent="filter()">
7
7
  <div class="qas-filters__input-content">
8
- <qas-input v-model="search" class="bg-white q-px-sm rounded-borders-sm shadow-2" data-cy="filters-search-input" :debounce="debounce" dense hide-bottom-space input-class="ellipsis text-grey-8" :outlined="false" :placeholder="searchPlaceholder" type="search">
8
+ <qas-input v-model="search" class="bg-white q-px-sm rounded-borders-sm shadow-2" data-cy="filters-search-input" :debounce="debounce" dense hide-bottom-space input-class="ellipsis text-grey-8" :outlined="false" :placeholder="searchPlaceholder" type="search" @update:model-value="onSearch">
9
9
  <template #prepend>
10
10
  <q-icon v-if="useSearchOnType" color="grey-8" name="sym_r_search" />
11
11
  <qas-btn v-else color="grey-9" icon="sym_r_search" variant="tertiary" @click="filter()" />
@@ -14,33 +14,11 @@
14
14
  <template #append>
15
15
  <qas-btn v-if="hasSearch" class="q-mr-sm" color="grey-9" icon="sym_r_clear" variant="tertiary" @click="clearSearch" />
16
16
 
17
- <qas-btn v-if="useFilterButton" :color="filterButtonColor" data-cy="filters-btn" icon="sym_r_tune" variant="tertiary">
18
- <q-menu anchor="center right" class="full-width" max-width="270px" self="top right">
19
- <div v-if="isFetching" class="q-pa-xl text-center">
20
- <q-spinner color="grey" size="2em" />
21
- </div>
22
-
23
- <div v-else-if="hasFetchError" class="q-pa-xl text-center">
24
- <q-icon color="negative" name="sym_r_warning" size="2em" />
25
- </div>
26
-
27
- <q-form v-else class="q-gutter-y-md q-pa-md" @submit.prevent="filter()">
28
- <div v-for="(field, index) in fields" :key="index">
29
- <qas-field v-model="filters[field.name]" :data-cy="`filters-${field.name}-field`" :field="field" v-bind="fieldsProps[field.name]" />
30
- </div>
31
-
32
- <div class="q-col-gutter-x-md q-mt-xl row">
33
- <div class="col-6">
34
- <qas-btn class="full-width" data-cy="filters-clear-btn" label="Limpar" variant="secondary" @click="clearFilters" />
35
- </div>
36
-
37
- <div class="col-6">
38
- <qas-btn class="full-width" data-cy="filters-submit-btn" label="Filtrar" type="submit" variant="primary" />
39
- </div>
40
- </div>
41
- </q-form>
42
- </q-menu>
43
- </qas-btn>
17
+ <template v-if="showFilterButton">
18
+ <slot :context="mx_context" :filter="filter" :filters="activeFilters" name="filter-button" :remove-filter="removeFilter">
19
+ <pv-filters-button v-if="useFilterButton" v-model="filters" v-bind="filterButtonProps" />
20
+ </slot>
21
+ </template>
44
22
  </template>
45
23
  </qas-input>
46
24
  </div>
@@ -48,6 +26,12 @@
48
26
  </slot>
49
27
  </div>
50
28
 
29
+ <div v-else-if="showFilterButton" class="col-12 col-md-6">
30
+ <slot :context="mx_context" :filter="filter" :filters="activeFilters" name="filter-button" :remove-filter="removeFilter">
31
+ <pv-filters-button v-if="useFilterButton" v-model="filters" v-bind="filterButtonProps" />
32
+ </slot>
33
+ </div>
34
+
51
35
  <div class="col-12 col-md-6">
52
36
  <slot name="right-side" />
53
37
  </div>
@@ -63,8 +47,8 @@
63
47
  </template>
64
48
 
65
49
  <script>
66
- import QasField from '../field/QasField.vue'
67
50
  import QasBtn from '../btn/QasBtn.vue'
51
+ import PvFiltersButton from './private/PvFiltersButton.vue'
68
52
 
69
53
  import { camelize, camelizeKeys } from 'humps'
70
54
  import { humanize, parseValue } from '../../helpers/filters.js'
@@ -76,7 +60,7 @@ export default {
76
60
 
77
61
  components: {
78
62
  QasBtn,
79
- QasField
63
+ PvFiltersButton
80
64
  },
81
65
 
82
66
  mixins: [contextMixin],
@@ -124,13 +108,24 @@ export default {
124
108
 
125
109
  useForceRefetch: {
126
110
  type: Boolean
111
+ },
112
+
113
+ useSpacing: {
114
+ default: true,
115
+ type: Boolean
116
+ },
117
+
118
+ useUpdateRoute: {
119
+ default: true,
120
+ type: Boolean
127
121
  }
128
122
  },
129
123
 
130
- emits: ['fetch-success', 'fetch-error'],
124
+ emits: ['fetch-success', 'fetch-error', 'update:currentFilters'],
131
125
 
132
126
  data () {
133
127
  return {
128
+ currentFilters: {},
134
129
  filters: {},
135
130
  hasFetchError: false,
136
131
  isFetching: false,
@@ -147,7 +142,7 @@ export default {
147
142
  const activeFilters = {}
148
143
 
149
144
  const fields = Object.keys(this.fields)
150
- const filters = camelizeKeys(this.mx_context.filters)
145
+ const filters = camelizeKeys(this.useUpdateRoute ? this.mx_context.filters : this.currentFilters)
151
146
 
152
147
  for (const key in filters) {
153
148
  const hasField = fields.includes(key)
@@ -171,13 +166,27 @@ export default {
171
166
  return getState.call(this, { entity: this.entity, key: 'filters' })
172
167
  },
173
168
 
169
+ filtersClass () {
170
+ return {
171
+ 'q-mb-xl': this.useSpacing
172
+ }
173
+ },
174
+
174
175
  filterButtonColor () {
175
176
  return this.hasActiveFilters ? 'primary' : 'grey-9'
176
177
  },
177
178
 
178
- // TODO: remover
179
- filterButtonLabel () {
180
- return this.$q.screen.gt.xs ? 'Filtrar' : undefined
179
+ filterButtonProps () {
180
+ return {
181
+ color: this.filterButtonColor,
182
+ error: this.hasFetchError,
183
+ fields: this.fields,
184
+ fieldsProps: this.fieldsProps,
185
+ loading: this.isFetching,
186
+
187
+ onClear: this.clearFilters,
188
+ onFilter: () => this.filter()
189
+ }
181
190
  },
182
191
 
183
192
  hasActiveFilters () {
@@ -213,13 +222,7 @@ export default {
213
222
  $route (to, from) {
214
223
  if (to.name === from.name) {
215
224
  this.fetchFilters()
216
- this.updateValues()
217
- }
218
- },
219
-
220
- search () {
221
- if (this.debounce) {
222
- this.filter()
225
+ this.useUpdateRoute && this.updateValues()
223
226
  }
224
227
  }
225
228
  },
@@ -233,11 +236,15 @@ export default {
233
236
  methods: {
234
237
  clearFilters () {
235
238
  const { filters, ...query } = this.mx_context
239
+ const activeFilters = {
240
+ ...filters,
241
+ ...this.filters
242
+ }
236
243
 
237
244
  if (this.hasFields) {
238
245
  const fields = Object.keys(this.fields)
239
246
 
240
- for (const key in filters) {
247
+ for (const key in activeFilters) {
241
248
  const camelizedKey = camelize(key)
242
249
  const hasField = fields.includes(camelizedKey)
243
250
 
@@ -250,7 +257,8 @@ export default {
250
257
  this.filters = {}
251
258
  }
252
259
 
253
- this.$router.push({ query })
260
+ this.updateCurrentFilters()
261
+ this.updateRouteQuery(query)
254
262
  },
255
263
 
256
264
  clearSearch () {
@@ -308,7 +316,8 @@ export default {
308
316
  search: this.search || undefined
309
317
  }
310
318
 
311
- this.$router.push({ query })
319
+ this.updateCurrentFilters()
320
+ this.updateRouteQuery(query)
312
321
  },
313
322
 
314
323
  getChipValue (value) {
@@ -321,7 +330,21 @@ export default {
321
330
  delete query[name]
322
331
  delete this.filters[name]
323
332
 
324
- this.$router.push({ query })
333
+ this.updateCurrentFilters()
334
+ this.updateRouteQuery(query)
335
+ },
336
+
337
+ updateCurrentFilters () {
338
+ this.currentFilters = {
339
+ ...this.filters,
340
+ ...(this.search && { search: this.search })
341
+ }
342
+
343
+ this.$emit('update:currentFilters', this.currentFilters)
344
+ },
345
+
346
+ updateRouteQuery (query) {
347
+ this.useUpdateRoute && this.$router.push({ query })
325
348
  },
326
349
 
327
350
  updateValues () {
@@ -341,16 +364,27 @@ export default {
341
364
  },
342
365
 
343
366
  watchOnceFields () {
367
+ if (!this.useUpdateRoute) return
368
+
344
369
  const watchOnce = this.$watch('fields', values => {
345
370
  if (Object.keys(values).length) {
346
371
  this.updateValues()
372
+ this.updateCurrentFilters()
347
373
  watchOnce()
348
374
  }
349
375
  })
350
376
  },
351
377
 
352
378
  handleSearchModelOnCreate () {
353
- !this.useFilterButton && this.setSearch()
379
+ if (this.useUpdateRoute && !this.useFilterButton) {
380
+ this.setSearch()
381
+ }
382
+ },
383
+
384
+ onSearch () {
385
+ if (this.debounce) {
386
+ this.filter()
387
+ }
354
388
  },
355
389
 
356
390
  setSearch () {
@@ -24,7 +24,7 @@ props:
24
24
  desc: Envia como parâmetro para a action "fetchFilters" do modulo correspondente a "entity".
25
25
  type: String
26
26
 
27
- use-badges:
27
+ use-chip:
28
28
  desc: Habilita ou não os "chips" mostrando os filtros ativos.
29
29
  default: true
30
30
  type: Boolean
@@ -38,6 +38,11 @@ props:
38
38
  desc: Força refazer o "fetch" mesmo caso já exista dados na store de filters.
39
39
  type: Boolean
40
40
 
41
+ use-update-route:
42
+ desc: Habilita ou não a atualização da rota com base nos filtros.
43
+ default: true
44
+ type: Boolean
45
+
41
46
  use-search-on-type:
42
47
  desc: Habilita ou não o filtro de busca sempre que o usuário digita.
43
48
  default: true
@@ -48,28 +53,48 @@ props:
48
53
  default: true
49
54
  type: Boolean
50
55
 
56
+ use-spacing:
57
+ desc: Habilita ou não o espaçamento padrão do componente.
58
+ default: true
59
+ type: Boolean
60
+
51
61
  slots:
52
- search:
53
- desc: Slot para seção do campo de busca.
62
+ default:
63
+ desc: Slot para acessar o final do componente.
54
64
  scope:
55
65
  filter:
56
- desc: função usada para fazer o filtro
66
+ desc: Função usada para realizar o filtro.
57
67
  type: Function
58
68
 
59
- default:
60
- desc: Slot para acessar o final do componente.
69
+ filters:
70
+ desc: Objeto que retorna os filtros ativos.
71
+ default: {}
72
+ type: Object
73
+
74
+ remove-filter:
75
+ desc: Função usada para remover os filtro.
76
+ type: Function
77
+ examples: ["removeFilter({ name: '[nome-do-filtro]' })"]
78
+
79
+ context:
80
+ desc: Contexto referente a query da url.
81
+ default: { filters: {}, page: 1 }
82
+ type: Object
83
+
84
+ filter-button:
85
+ desc: Slot para seção do botão de filtro.
61
86
  scope:
62
87
  filter:
63
- desc: Função usada para fazer o filtro
88
+ desc: Função usada para realizar o filtro.
64
89
  type: Function
65
90
 
66
91
  filters:
67
- desc: Objeto que retorna os filtros ativos
92
+ desc: Objeto que retorna os filtros ativos.
68
93
  default: {}
69
94
  type: Object
70
95
 
71
96
  remove-filter:
72
- desc: Função usada para remover os filtro
97
+ desc: Função usada para remover os filtro.
73
98
  type: Function
74
99
  examples: ["removeFilter({ name: '[nome-do-filtro]' })"]
75
100
 
@@ -78,6 +103,16 @@ slots:
78
103
  default: { filters: {}, page: 1 }
79
104
  type: Object
80
105
 
106
+ right-side:
107
+ desc: Slot para a seção na lateral direita.
108
+
109
+ search:
110
+ desc: Slot para seção do campo de busca.
111
+ scope:
112
+ filter:
113
+ desc: função usada para realizar o filtro.
114
+ type: Function
115
+
81
116
  events:
82
117
  '@fetch-success -> function(value)':
83
118
  desc: Dispara quando a action "fetchFilters" é executada com sucesso.
@@ -92,3 +127,10 @@ events:
92
127
  value:
93
128
  desc: Retorna todos os dados "cru" respondido na exceção do fetch.
94
129
  type: Object
130
+
131
+ '@update:currentFilters -> function(value)':
132
+ desc: Dispara quando é realizado ou removido uma pesquisa no campo de busca e quando é clicado no botão "Filtrar" ou "Limpar" do filtro.
133
+ params:
134
+ value:
135
+ desc: Retorna todos os filtros realizados.
136
+ type: Object
@@ -0,0 +1,89 @@
1
+ <template>
2
+ <qas-btn :color="color" data-cy="filters-btn" icon="sym_r_tune" variant="tertiary">
3
+ <q-menu anchor="center right" class="full-width" max-width="270px" self="top right">
4
+ <div v-if="loading" class="q-pa-xl text-center">
5
+ <q-spinner color="grey" size="2em" />
6
+ </div>
7
+
8
+ <div v-else-if="error" class="q-pa-xl text-center">
9
+ <q-icon color="negative" name="sym_r_warning" size="2em" />
10
+ </div>
11
+
12
+ <q-form v-else class="q-gutter-y-md q-pa-md" @submit.prevent="$emit('filter')">
13
+ <div v-for="(field, index) in fields" :key="index">
14
+ <qas-field v-model="filters[field.name]" :data-cy="`filters-${field.name}-field`" :field="field" v-bind="fieldsProps[field.name]" />
15
+ </div>
16
+
17
+ <qas-actions gutter="sm" use-equal-width>
18
+ <template #primary>
19
+ <qas-btn class="full-width" data-cy="filters-submit-btn" label="Filtrar" type="submit" variant="primary" />
20
+ </template>
21
+ <template #secondary>
22
+ <qas-btn class="full-width" data-cy="filters-clear-btn" label="Limpar" variant="secondary" @click="$emit('clear')" />
23
+ </template>
24
+ </qas-actions>
25
+ </q-form>
26
+ </q-menu>
27
+ </qas-btn>
28
+ </template>
29
+
30
+ <script>
31
+ import QasActions from '../../actions/QasActions.vue'
32
+ import QasBtn from '../../btn/QasBtn.vue'
33
+ import QasField from '../../field/QasField.vue'
34
+
35
+ export default {
36
+ name: 'PvFiltersButton',
37
+
38
+ components: {
39
+ QasActions,
40
+ QasBtn,
41
+ QasField
42
+ },
43
+
44
+ props: {
45
+ color: {
46
+ type: String,
47
+ default: 'grey-9',
48
+ validator: value => ['grey-9', 'primary', 'white'].includes(value)
49
+ },
50
+
51
+ error: {
52
+ type: Boolean
53
+ },
54
+
55
+ fields: {
56
+ default: () => ({}),
57
+ type: Object
58
+ },
59
+
60
+ fieldsProps: {
61
+ default: () => ({}),
62
+ type: Object
63
+ },
64
+
65
+ loading: {
66
+ type: Boolean
67
+ },
68
+
69
+ modelValue: {
70
+ default: () => ({}),
71
+ type: Object
72
+ }
73
+ },
74
+
75
+ emits: ['clear', 'filter', 'update:modelValue'],
76
+
77
+ computed: {
78
+ filters: {
79
+ get () {
80
+ return this.modelValue
81
+ },
82
+
83
+ set (value) {
84
+ return this.$emit('update:modelValue', value)
85
+ }
86
+ }
87
+ }
88
+ }
89
+ </script>
@@ -420,6 +420,8 @@ export default {
420
420
  NotifySuccess(response.data.status.text || this.defaultNotifyMessages.success)
421
421
  this.$emit('submit-success', response, this.modelValue)
422
422
 
423
+ this.createSubmitSuccessEvent({ ...payload, entity: this.entity })
424
+
423
425
  this.$qas.logger.group(
424
426
  `QasFormView - submit -> resposta da action ${this.entity}/${this.mode}`, [response]
425
427
  )
@@ -449,6 +451,16 @@ export default {
449
451
  }
450
452
  },
451
453
 
454
+ createSubmitSuccessEvent (detail = {}) {
455
+ const event = new CustomEvent('submit-success', {
456
+ bubbles: false,
457
+ cancelable: false,
458
+ detail
459
+ })
460
+
461
+ window.dispatchEvent(event)
462
+ },
463
+
452
464
  setIgnoreRouterGuard ({ detail: { id, entity } }) {
453
465
  this.ignoreRouterGuard = this.id === id && this.entity === entity
454
466
  }
@@ -0,0 +1,116 @@
1
+ <!-- TODO: componente adaptado somente para o uso do QasUploader, precisa ser revisitado para ser implementado no QasGallery -->
2
+ <template>
3
+ <div class="bg-white q-pa-md qas-gallery-card rounded-borders shadow-2" :class="classes">
4
+ <header class="flat items-center no-wrap q-mb-md row" :class="headerClasses">
5
+ <slot name="header">
6
+ <div class="ellipsis q-mr-xs qas-gallery__name">
7
+ <slot v-if="card.name" name="name">
8
+ {{ card.name }}
9
+ </slot>
10
+ </div>
11
+
12
+ <div v-if="hasActionsMenu">
13
+ <qas-actions-menu v-bind="defaultActionsMenuProps" />
14
+ </div>
15
+ </slot>
16
+ </header>
17
+
18
+ <div class="qas-gallery-card__image">
19
+ <slot name="image">
20
+ <q-img class="rounded-borders" height="100%" :src="card.url" v-bind="imageProps">
21
+ <template #error>
22
+ <slot name="image-error" />
23
+ </template>
24
+ </q-img>
25
+ </slot>
26
+ </div>
27
+
28
+ <div v-if="hasBottom" class="q-mt-md">
29
+ <slot name="bottom">
30
+ <qas-grid-generator v-if="hasGridGenerator" v-bind="gridGeneratorProps" />
31
+ </slot>
32
+ </div>
33
+ </div>
34
+ </template>
35
+
36
+ <script>
37
+ export default {
38
+ name: 'QasGalleryCard',
39
+
40
+ props: {
41
+ actionsMenuProps: {
42
+ type: Object,
43
+ default: () => ({})
44
+ },
45
+
46
+ card: {
47
+ type: Object,
48
+ default: () => ({})
49
+ },
50
+
51
+ disable: {
52
+ type: Boolean
53
+ },
54
+
55
+ gridGeneratorProps: {
56
+ type: Object,
57
+ default: () => ({})
58
+ },
59
+
60
+ imageProps: {
61
+ type: Object,
62
+ default: () => ({})
63
+ }
64
+ },
65
+
66
+ computed: {
67
+ classes () {
68
+ return {
69
+ 'text-grey-6': this.disable
70
+ }
71
+ },
72
+
73
+ defaultActionsMenuProps () {
74
+ const { buttonProps } = this.actionsMenuProps
75
+
76
+ return {
77
+ useLabel: false,
78
+ ...this.actionsMenuProps,
79
+
80
+ buttonProps: {
81
+ disable: this.disable,
82
+ ...buttonProps
83
+ }
84
+ }
85
+ },
86
+
87
+ hasActionsMenu () {
88
+ return !!Object.keys(this.actionsMenuProps).length
89
+ },
90
+
91
+ hasBottom () {
92
+ return !!this.$slots.bottom || this.hasGridGenerator
93
+ },
94
+
95
+ hasGridGenerator () {
96
+ return !!Object.keys(this.gridGeneratorProps).length
97
+ },
98
+
99
+ headerClasses () {
100
+ return {
101
+ 'justify-between': this.card.name,
102
+ 'justify-right': !this.card.name,
103
+ 'text-grey-9': !this.disable
104
+ }
105
+ }
106
+ }
107
+ }
108
+ </script>
109
+
110
+ <style lang="scss">
111
+ .qas-gallery-card {
112
+ &__image {
113
+ height: 180px;
114
+ }
115
+ }
116
+ </style>
@@ -0,0 +1,45 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Componente de card de galeria (utilizado no QasUploader, QasGallery e nos produtos).
5
+
6
+ props:
7
+ actions-menu-props:
8
+ desc: Propriedades repassadas para o QasActionsMenu.
9
+ default: {}
10
+ type: Object
11
+
12
+ card:
13
+ desc: Informações do card, como name (nome) e url.
14
+ default: {}
15
+ type: Object
16
+ examples: ["{ name: 'meu nome', url: 'minha-url-da-imagem' }"]
17
+
18
+ disable:
19
+ desc: Propriedade que aplica estilos de desabilitado.
20
+ type: Boolean
21
+
22
+ grid-generator-props:
23
+ desc: Propriedades repassadas para o QasGridGenerator.
24
+ type: Boolean
25
+
26
+ image-props:
27
+ desc: Propriedades repassadas para o QImg.
28
+ default: {}
29
+ type: Object
30
+
31
+ slots:
32
+ bottom:
33
+ desc: Slot para acessar o conteúdo abaixo da imagem.
34
+
35
+ image:
36
+ desc: Slot para acessar o conteúdo da imagem (onde fica o QasGridGenerator).
37
+
38
+ image-error:
39
+ desc: Slot para acessar o conteúdo da imagem quando existe algum erro com a imagem.
40
+
41
+ header:
42
+ desc: Slot para acessar o conteúdo do cabeçalho.
43
+
44
+ name:
45
+ desc: Slot para acessar o conteúdo do nome, acima da imagem.
@@ -61,11 +61,6 @@ export default {
61
61
  computed: {
62
62
  formattedFields () {
63
63
  if (this.useEmptyResult) {
64
- this.$qas.logger.group(
65
- 'QasGridGenerator - formattedFields -> this.useEmptyResult tem valor "true"',
66
- [this.fields]
67
- )
68
-
69
64
  return this.fields
70
65
  }
71
66