@bildvitta/quasar-ui-asteroid 3.12.0-beta.9 → 3.13.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bildvitta/quasar-ui-asteroid",
3
3
  "description": "Asteroid",
4
- "version": "3.12.0-beta.9",
4
+ "version": "3.13.0-beta.0",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "dist/asteroid.cjs.min.js",
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="cursor-pointer items-center no-wrap q-gutter-sm qas-app-user row">
2
+ <div class="cursor-pointer items-center no-wrap q-gutter-sm qas-app-user row" data-cy="app-user">
3
3
  <div class="relative-position">
4
4
  <qas-avatar :image="user.photo" :size="avatarSize" :title="userName" />
5
5
  <q-badge v-if="hasNotifications" color="red" floating>{{ notifications.count }}</q-badge>
@@ -17,7 +17,7 @@
17
17
  <div class="ellipsis qas-app-user__menu-name">{{ userName }}</div>
18
18
  <div class="ellipsis">{{ user.email }}</div>
19
19
 
20
- <qas-select v-if="hasCompaniesSelect" v-model="companiesModel" class="q-my-md" label="Vínculo" :loading="loading" :options="companiesOptions" @update:model-value="setCompanies" />
20
+ <qas-select v-if="hasCompaniesSelect" v-model="companiesModel" class="q-my-md" data-cy="app-user-companies-select" label="Vínculo" :loading="loading" :options="companiesOptions" @update:model-value="setCompanies" />
21
21
 
22
22
  <q-list class="q-mt-md">
23
23
  <q-item v-close-popup :active="false" class="qas-app-user__menu-item" clickable :to="user.to">
@@ -12,26 +12,26 @@
12
12
  </header>
13
13
 
14
14
  <section class="text-body1 text-grey-8">
15
- <component :is="componentTag" ref="form">
15
+ <component :is="componentTag" ref="form" v-bind="componentProps">
16
16
  <slot name="description">
17
17
  <component :is="descriptionComponentTag">{{ card.description }}</component>
18
18
  </slot>
19
+
20
+ <div v-if="!isInfoDialog">
21
+ <slot name="actions">
22
+ <qas-actions v-bind="formattedActionsProps">
23
+ <template v-if="hasOk" #primary>
24
+ <qas-btn v-close-popup="!useForm" class="full-width" variant="primary" v-bind="defaultOk" />
25
+ </template>
26
+
27
+ <template v-if="hasCancel" #secondary>
28
+ <qas-btn v-close-popup class="full-width" v-bind="defaultCancel" variant="secondary" />
29
+ </template>
30
+ </qas-actions>
31
+ </slot>
32
+ </div>
19
33
  </component>
20
34
  </section>
21
-
22
- <footer v-if="!isInfoDialog">
23
- <slot name="actions">
24
- <qas-actions v-bind="formattedActionsProps">
25
- <template v-if="hasOk" #primary>
26
- <qas-btn v-close-popup="!useForm" class="full-width" variant="primary" v-bind="defaultOk" @click="submitHandler" />
27
- </template>
28
-
29
- <template v-if="hasCancel" #secondary>
30
- <qas-btn v-close-popup class="full-width" v-bind="defaultCancel" variant="secondary" />
31
- </template>
32
- </qas-actions>
33
- </slot>
34
- </footer>
35
35
  </div>
36
36
  </q-dialog>
37
37
  </template>
@@ -106,8 +106,13 @@ export default {
106
106
  },
107
107
 
108
108
  emits: [
109
+ // model
109
110
  'update:modelValue',
110
- 'validate'
111
+
112
+ // actions
113
+ 'validate',
114
+ 'ok',
115
+ 'cancel'
111
116
  ],
112
117
 
113
118
  computed: {
@@ -115,7 +120,10 @@ export default {
115
120
  return {
116
121
  label: 'Cancelar',
117
122
  outline: true,
118
- ...this.cancel
123
+
124
+ ...this.cancel,
125
+
126
+ onClick: this.onCancel
119
127
  }
120
128
  },
121
129
 
@@ -123,7 +131,11 @@ export default {
123
131
  return {
124
132
  label: 'Ok',
125
133
  type: this.ok?.type || this.useForm ? 'submit' : 'button',
126
- ...this.ok
134
+
135
+ ...this.ok,
136
+
137
+ // adiciona somente se não estiver usando useForm pois o controle ficará no submit.
138
+ ...(!this.useForm && { onClick: this.onOk })
127
139
  }
128
140
  },
129
141
 
@@ -139,6 +151,16 @@ export default {
139
151
  return this.useForm ? 'q-form' : 'div'
140
152
  },
141
153
 
154
+ componentProps () {
155
+ /**
156
+ * adiciona evento de submit caso useForm seja true,
157
+ * uma vez que somente o q-form possui este evento.
158
+ */
159
+ return {
160
+ ...(this.useForm && { onSubmit: this.onSubmit })
161
+ }
162
+ },
163
+
142
164
  dialogProps () {
143
165
  return {
144
166
  ...(!this.usePlugin && { modelValue: this.modelValue }),
@@ -235,6 +257,29 @@ export default {
235
257
 
236
258
  updateModelValue (value) {
237
259
  this.$emit('update:modelValue', value)
260
+ },
261
+
262
+ onOk () {
263
+ this.ok?.onClick?.()
264
+ this.$emit('ok')
265
+ },
266
+
267
+ onCancel () {
268
+ this.cancel?.onClick?.()
269
+ this.$emit('cancel')
270
+ },
271
+
272
+ /**
273
+ * Sem este método, ao clicar enter com a prop useForm ativada a tela era recarregada,
274
+ * e a ação de click do botão não era chamada pois ele não esta dentro do form.
275
+ */
276
+ onSubmit (event) {
277
+ event.preventDefault()
278
+
279
+ if (this.hasOk) {
280
+ this.onOk()
281
+ this.submitHandler()
282
+ }
238
283
  }
239
284
  }
240
285
  }
@@ -83,3 +83,10 @@ events:
83
83
  value:
84
84
  desc: Retorna se os campos passou ou não na validação
85
85
  type: Boolean
86
+
87
+ '@cancel: -> function ()':
88
+ desc: Dispara toda vez que é clicado no botão "cancel".
89
+
90
+ '@ok: -> function ()':
91
+ desc: Dispara toda vez que é clicado no botão "ok" ou quando useForm for true e o for clicado "enter" estando com foco em algum input (evento de submit).
92
+
@@ -213,6 +213,10 @@ export default {
213
213
  watch: {
214
214
  isSubmitting (value) {
215
215
  this.$emit('update:submitting', value)
216
+ },
217
+
218
+ '$route.path' () {
219
+ this.ignoreRouterGuard = false
216
220
  }
217
221
  },
218
222
 
@@ -374,10 +378,9 @@ export default {
374
378
  if (!this.ignoreKeysInUnsavedChanges.length) return
375
379
 
376
380
  this.ignoreKeysInUnsavedChanges.forEach(key => {
377
- if (!firstValue) return
381
+ if (firstValue) delete firstValue[key]
378
382
 
379
- delete firstValue[key]
380
- delete secondValue[key]
383
+ if (secondValue) delete secondValue[key]
381
384
  })
382
385
  },
383
386
 
@@ -426,14 +429,16 @@ export default {
426
429
  payload
427
430
  })
428
431
 
432
+ const modelValue = { ...this.modelValue, ...response.data.result }
433
+
429
434
  if (this.useDialogOnUnsavedChanges) {
430
- this.cachedResult = extend(true, {}, this.modelValue)
435
+ this.cachedResult = extend(true, {}, modelValue)
431
436
  }
432
437
 
433
438
  this.mx_setErrors()
434
439
  this.$emit('update:errors', this.mx_errors)
435
440
 
436
- NotifySuccess(response.data.status.text || this.defaultNotifyMessages.success)
441
+ this.$emit('update:modelValue', modelValue)
437
442
  this.$emit('submit-success', response, this.modelValue)
438
443
 
439
444
  this.createSubmitSuccessEvent({ ...payload, entity: this.entity })
@@ -441,6 +446,8 @@ export default {
441
446
  this.$qas.logger.group(
442
447
  `QasFormView - submit -> resposta da action ${this.entity}/${this.mode}`, [response]
443
448
  )
449
+
450
+ NotifySuccess(response.data.status.text || this.defaultNotifyMessages.success)
444
451
  } catch (error) {
445
452
  const errors = error?.response?.data?.errors
446
453
  const message = error?.response?.data?.status?.text
@@ -157,17 +157,21 @@ slots:
157
157
  desc: Slot para acessar o header.
158
158
 
159
159
  events:
160
- '@fetch-success -> function(value)':
160
+ '@fetch-success -> function(response, modelValue)':
161
161
  desc: Dispara quando a action "fetchSingle" é executada com sucesso.
162
162
  params:
163
- value:
163
+ response:
164
164
  desc: Retorna todos os dados "cru" respondido pelo fetch.
165
165
  type: Object
166
166
 
167
- '@fetch-error -> function(value)':
167
+ modelValue:
168
+ desc: Retorna o model anterior ao fetch.
169
+ type: Object
170
+
171
+ '@fetch-error -> function(error)':
168
172
  desc: Dispara quando a action "fetchSingle" cai em uma exceção.
169
173
  params:
170
- value:
174
+ error:
171
175
  desc: Retorna todos os dados "cru" respondido na exceção do fetch.
172
176
  type: Object
173
177
 
@@ -212,3 +216,23 @@ events:
212
216
  value:
213
217
  desc: Retorna se está ou não fazendo fetching de dados.
214
218
  type: Boolean
219
+
220
+ '@submit-success -> function(response, modelValue)':
221
+ desc: Dispara quando a action "submit" é executada com sucesso.
222
+ params:
223
+ response:
224
+ desc: Retorna todos os dados "cru" respondido pelo submit.
225
+ type: Object
226
+
227
+ modelValue:
228
+ desc: Retorna o model anterior ao submit.
229
+ type: Object
230
+
231
+ '@submit-error -> function(error)':
232
+ desc: Dispara quando a action "submit" cai em uma exceção.
233
+ params:
234
+ error:
235
+ desc: Retorna todos os dados "cru" respondido na exceção do submit.
236
+ type: Object
237
+
238
+
@@ -0,0 +1,163 @@
1
+ <template>
2
+ <div class="qas-infinite-scroll" :style="containerStyle">
3
+ <q-infinite-scroll
4
+ ref="infiniteScroll"
5
+ v-bind="attributes"
6
+ @load="onLoad"
7
+ >
8
+ <slot />
9
+
10
+ <template #loading>
11
+ <div class="justify-center q-my-md row">
12
+ <q-spinner-dots
13
+ color="primary"
14
+ size="3em"
15
+ />
16
+ </div>
17
+ </template>
18
+ </q-infinite-scroll>
19
+
20
+ <qas-empty-result-text v-if="hasNoResults" />
21
+ </div>
22
+ </template>
23
+
24
+ <script setup>
25
+ import { ref, computed, inject } from 'vue'
26
+ import { NotifyError } from '../../plugins'
27
+
28
+ defineOptions({ name: 'QasInfiniteScroll' })
29
+
30
+ const props = defineProps({
31
+ list: {
32
+ type: Array,
33
+ default: () => []
34
+ },
35
+
36
+ limitPerPage: {
37
+ type: Number,
38
+ default: 12
39
+ },
40
+
41
+ url: {
42
+ type: String,
43
+ default: '',
44
+ required: true
45
+ },
46
+
47
+ params: {
48
+ type: Object,
49
+ default: () => ({})
50
+ },
51
+
52
+ infiniteScrollProps: {
53
+ type: Object,
54
+ default: () => ({})
55
+ },
56
+
57
+ maxHeight: {
58
+ type: String,
59
+ default: ''
60
+ }
61
+ })
62
+
63
+ defineExpose({ refresh, remove })
64
+
65
+ const emits = defineEmits(['update:list'])
66
+
67
+ const axios = inject('axios')
68
+
69
+ const infiniteScroll = ref(null)
70
+
71
+ const hasFetchingError = ref(false)
72
+ const isFetching = ref(false)
73
+ const hasMadeFirstFetch = ref(false)
74
+ const count = ref(0)
75
+ const offset = ref(0)
76
+
77
+ const listLength = computed(() => model.value.length)
78
+
79
+ const attributes = computed(() => ({
80
+ offset: 100,
81
+ debounce: 0,
82
+ ...(props.maxHeight && { scrollTarget: '.qas-infinite-scroll' }),
83
+ ...props.infiniteScrollProps
84
+ }))
85
+
86
+ const isEmptyList = computed(() => !listLength.value && !isFetching.value)
87
+
88
+ const hasNoResults = computed(() => isEmptyList.value && hasMadeFirstFetch.value)
89
+
90
+ const containerStyle = computed(() => ({
91
+ ...(props.maxHeight && { maxHeight: props.maxHeight, overflow: 'auto' })
92
+ }))
93
+
94
+ const model = computed({
95
+ get () {
96
+ return props.list
97
+ },
98
+
99
+ set (newList) {
100
+ emits('update:list', newList)
101
+ }
102
+ })
103
+
104
+ async function onLoad (_, done) {
105
+ const hasMadeFirstFetchAndHasNoData = hasMadeFirstFetch.value && !listLength.value
106
+ const hasFetchAllData = listLength.value && listLength.value >= count.value
107
+ const stop = hasFetchingError.value || isFetching.value || hasMadeFirstFetchAndHasNoData || hasFetchAllData
108
+
109
+ if (stop) {
110
+ infiniteScroll.value.stop()
111
+ } else {
112
+ infiniteScroll.value.resume()
113
+ await fetchList()
114
+ }
115
+
116
+ done()
117
+ }
118
+
119
+ async function fetchList () {
120
+ try {
121
+ isFetching.value = true
122
+
123
+ const { data } = await axios.get(props.url, {
124
+ params: { offset: offset.value, limit: props.limitPerPage, ...props.params }
125
+ })
126
+
127
+ const newList = [...model.value, ...(data.results || [])]
128
+
129
+ model.value = newList
130
+ offset.value = newList.length
131
+ count.value = data.count
132
+
133
+ /**
134
+ * Sinalizar que houve já uma busca, para evitar que onLoad entre em looping,
135
+ * após buscar uma vez e retornar uma lista vazia.
136
+ */
137
+ hasMadeFirstFetch.value = true
138
+ } catch {
139
+ NotifyError('Ops… Não conseguimos acessar as informações. Por favor, tente novamente em alguns minutos.')
140
+
141
+ hasFetchingError.value = true
142
+ } finally {
143
+ isFetching.value = false
144
+ }
145
+ }
146
+
147
+ function refresh () {
148
+ count.value = 0
149
+ offset.value = 0
150
+ model.value = []
151
+
152
+ hasMadeFirstFetch.value = false
153
+
154
+ infiniteScroll.value.reset()
155
+ infiniteScroll.value.resume()
156
+ }
157
+
158
+ function remove (index) {
159
+ model.value.splice(index, 1)
160
+ count.value -= 1
161
+ offset.value -= 1
162
+ }
163
+ </script>
@@ -0,0 +1,49 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Componente de infinite scroll que implementa o "QInfiniteScroll".
5
+
6
+ props:
7
+ list:
8
+ desc: Model da lista de itens.
9
+ default: []
10
+ type: Array
11
+ examples: [v-model:list="list"]
12
+ model: true
13
+
14
+ limitPerPage:
15
+ desc: é repassado na query da requição sendo responsavel pela quantidade de itens por busca.
16
+ default: 12
17
+ type: Number
18
+
19
+ url:
20
+ desc: URL utilizado para fazer a busca dos itens.
21
+ type: String
22
+
23
+ params:
24
+ desc: Parâmetros passados na requisição de busca dos itens.
25
+ default: {}
26
+ type: Object
27
+ examples: ["{ status: 'is_active' }"]
28
+
29
+ infinite-scroll-props:
30
+ desc: Propriedades repassadas para o QInfiniteScroll
31
+ default: {}
32
+ type: Object
33
+ examples: ["{ offset: 500 }"]
34
+
35
+ max-height:
36
+ desc: Seta a altura do container do infinite scroll, caso queira que o infinite scroll fique em um box por exemplo.
37
+ type: String
38
+
39
+ events:
40
+ '@update:list -> function (newList)':
41
+ desc: Dispara toda vez que é feito uma nova busca e a lista é atualizada.
42
+ params:
43
+ newList:
44
+ desc: Novo valor do list
45
+ type: Array
46
+
47
+ slots:
48
+ default:
49
+ desc: slot para exibir a lista na qual o componente fez a busca.
@@ -243,7 +243,7 @@ function useList () {
243
243
 
244
244
  model.value.push(...newModel)
245
245
 
246
- emit('add', newModel)
246
+ emit('add', normalizedItems)
247
247
 
248
248
  updateModel()
249
249
  }
@@ -76,21 +76,21 @@ slots:
76
76
  desc: Slot para substituir cabeçalho do dialog.
77
77
 
78
78
  events:
79
- 'add -> function(list)':
79
+ '@add -> function(items)':
80
80
  desc: Dispara toda vez que é adicionado novos itens.
81
81
  params:
82
- list:
82
+ items:
83
83
  desc: Lista de itens adicionados.
84
84
  type: Array
85
85
 
86
- 'remove -> function(value)':
86
+ '@remove -> function(value)':
87
87
  desc: Dispara toda vez que é removido um item.
88
88
  params:
89
89
  value:
90
90
  desc: Valor do item (uuid/id/slug).
91
91
  type: String
92
92
 
93
- 'update:model-value -> function(list)':
93
+ '@update:model-value -> function(list)':
94
94
  desc: Dispara toda vez que o model é atualizado.
95
95
  params:
96
96
  list:
@@ -2,7 +2,7 @@
2
2
  <div class="qas-tabs-generator">
3
3
  <q-tabs v-model="model" active-color="primary" align="left" :breakpoint="0" content-class="text-grey-8" dense inline-label left-icon="sym_r_chevron_left" outside-arrows right-icon="sym_r_chevron_right">
4
4
  <slot v-for="(tab, key) in formattedTabs" :item="tab" :name="`tab-${tab.value}`">
5
- <q-tab :key="key" v-bind="getTabProps(tab)" class="text-body1" :name="tab.value" no-caps :ripple="false">
5
+ <component :is="tabComponent" :key="key" v-bind="getTabProps(tab)" class="text-body1" :name="tab.value" no-caps :ripple="false">
6
6
  <slot :item="tab" :name="`tab-after-${tab.value}`">
7
7
  <q-icon v-if="tab.icon" :name="tab.icon" size="sm" />
8
8
 
@@ -12,7 +12,7 @@
12
12
  {{ getFormattedLabel(tab) }}
13
13
  </div>
14
14
  </slot>
15
- </q-tab>
15
+ </component>
16
16
  </slot>
17
17
  </q-tabs>
18
18
  </div>
@@ -45,6 +45,10 @@ export default {
45
45
  default: () => ({}),
46
46
  required: true,
47
47
  type: [Object, Array]
48
+ },
49
+
50
+ useRouteTab: {
51
+ type: Boolean
48
52
  }
49
53
  },
50
54
 
@@ -77,6 +81,10 @@ export default {
77
81
 
78
82
  this.$emit('update:modelValue', value)
79
83
  }
84
+ },
85
+
86
+ tabComponent () {
87
+ return this.useRouteTab ? 'q-route-tab' : 'q-tab'
80
88
  }
81
89
  },
82
90
 
@@ -24,6 +24,11 @@ props:
24
24
  type: [Object, Array]
25
25
  examples: ["{ tab1: 'tab1', tab2: 'tab2' }"]
26
26
 
27
+ useRouteTab:
28
+ desc: Quando "true" será utilizado o componente "QRouteTab" do Quasar (https://quasar.dev/vue-components/tabs#qroutetab-api).
29
+ default: false
30
+ type: Boolean
31
+
27
32
  slots:
28
33
  'tab-[nome-da-chave]':
29
34
  desc: Slot dinâmico gerado a partir das chave passada na prop "tabs", substitui todo o "q-tab".
@@ -154,6 +154,11 @@ export default {
154
154
  type: Number
155
155
  },
156
156
 
157
+ uploadCredentialsParams: {
158
+ type: Object,
159
+ default: () => ({})
160
+ },
161
+
157
162
  uploading: {
158
163
  type: Boolean
159
164
  },
@@ -170,6 +175,11 @@ export default {
170
175
  useResize: {
171
176
  default: true,
172
177
  type: Boolean
178
+ },
179
+
180
+ useUrlPath: {
181
+ type: Boolean,
182
+ default: true
173
183
  }
174
184
  },
175
185
 
@@ -366,7 +376,8 @@ export default {
366
376
  try {
367
377
  const { data } = await this.$axios.post('/upload-credentials/', {
368
378
  entity: this.entity,
369
- filename
379
+ filename,
380
+ ...this.uploadCredentialsParams
370
381
  })
371
382
 
372
383
  return data
@@ -377,7 +388,7 @@ export default {
377
388
  uploadedFiles = uploadedFiles.map((file, indexToDelete) => {
378
389
  return {
379
390
  isUploaded: true,
380
- url: file.xhr ? file.xhr.responseURL.split('?').shift() : '',
391
+ url: file.xhr ? this.getURLValue(file.xhr) : '',
381
392
  name: file.name,
382
393
  indexToDelete,
383
394
  isFailed: this.isFailed(file)
@@ -421,6 +432,16 @@ export default {
421
432
  return value.split('/').pop()
422
433
  },
423
434
 
435
+ /**
436
+ * Caso useUrlPath seja true, retorna de responseURL, senão tenta retornar o response
437
+ * que pode ser um base64, caso não tenha o response, retorna a responseURL.
438
+ */
439
+ getURLValue (xhr) {
440
+ const responseURL = xhr.responseURL.split('?').shift()
441
+
442
+ return this.useUrlPath ? responseURL : (xhr.response || responseURL)
443
+ },
444
+
424
445
  getModelValue (index) {
425
446
  if (!this.useObjectModel) return {}
426
447
 
@@ -571,7 +592,7 @@ export default {
571
592
  uploaded (response) {
572
593
  this.hasError = false
573
594
 
574
- const fullPath = response.xhr.responseURL.split('?').shift()
595
+ const fullPath = this.getURLValue(response.xhr)
575
596
 
576
597
  const objectValue = {
577
598
  format: response.files[0].type,
@@ -33,7 +33,7 @@ props:
33
33
  'image/webp',
34
34
  'image/jpg'
35
35
  ]
36
-
36
+
37
37
  columns:
38
38
  desc: Seta as colunas (grid cols), valor default depende se o componente é múltiplo ou não.
39
39
  default:
@@ -118,6 +118,11 @@ props:
118
118
  default: 1280
119
119
  type: Number
120
120
 
121
+ upload-credentials-params:
122
+ desc: Parâmetros enviados para a API do `/upload-credentials/`.
123
+ default: {}
124
+ type: Object
125
+
121
126
  uploading:
122
127
  desc: Model que retorna se o componente está fazendo algum upload.
123
128
  type: Boolean
@@ -139,6 +144,11 @@ props:
139
144
  default: true
140
145
  type: Boolean
141
146
 
147
+ use-url-path:
148
+ desc: Controla se o componente vai salvar o path url no model ou por exemplo um base64.
149
+ default: true
150
+ type: Boolean
151
+
142
152
  slots:
143
153
  header:
144
154
  desc: Acesso ao header do <q-uploader />.
package/src/vue-plugin.js CHANGED
@@ -28,6 +28,7 @@ import QasGallery from './components/gallery/QasGallery.vue'
28
28
  import QasGalleryCard from './components/gallery-card/QasGalleryCard.vue'
29
29
  import QasGridGenerator from './components/grid-generator/QasGridGenerator.vue'
30
30
  import QasHeaderActions from './components/header-actions/QasHeaderActions.vue'
31
+ import QasInfiniteScroll from './components/infinite-scroll/QasInfiniteScroll.vue'
31
32
  import QasInput from './components/input/QasInput.vue'
32
33
  import QasLabel from './components/label/QasLabel.vue'
33
34
  import QasLayout from './components/layout/QasLayout.vue'
@@ -111,6 +112,7 @@ async function install (app) {
111
112
  app.component('QasGalleryCard', QasGalleryCard)
112
113
  app.component('QasGridGenerator', QasGridGenerator)
113
114
  app.component('QasHeaderActions', QasHeaderActions)
115
+ app.component('QasInfiniteScroll', QasInfiniteScroll)
114
116
  app.component('QasInput', QasInput)
115
117
  app.component('QasLabel', QasLabel)
116
118
  app.component('QasLayout', QasLayout)
@@ -192,6 +194,7 @@ export {
192
194
  QasGalleryCard,
193
195
  QasGridGenerator,
194
196
  QasHeaderActions,
197
+ QasInfiniteScroll,
195
198
  QasInput,
196
199
  QasLabel,
197
200
  QasLayout,