@bildvitta/quasar-ui-asteroid 3.17.0-beta.23 → 3.17.0-beta.25

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.17.0-beta.23",
4
+ "version": "3.17.0-beta.25",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "dist/asteroid.cjs.min.js",
@@ -108,23 +108,29 @@ const props = defineProps({
108
108
  }
109
109
  })
110
110
 
111
+ // emits
111
112
  const emit = defineEmits(['sign-out', 'toggle-notifications'])
112
113
 
113
- // vindo direto do boot api.js
114
+ // globals
114
115
  const axios = inject('axios')
115
116
 
117
+ // composables
116
118
  const router = useRouter()
117
119
 
118
120
  const { isNotificationsEnabled, unreadNotificationsCount } = useNotifications()
119
121
 
120
122
  const { reset } = useQueryCache()
121
123
 
124
+ const { avatarNotificationCountProps } = useAvatarNotifications()
125
+
126
+ // refs
122
127
  const companiesModel = ref('')
123
128
  const loading = ref(false)
124
129
 
125
- const { avatarNotificationCountProps } = useAvatarNotifications()
130
+ // consts
131
+ const IS_ME_VERSION_2 = process.env.ME_VERSION === 2
126
132
 
127
- // computed
133
+ // computeds
128
134
  const defaultCompanyProps = computed(() => {
129
135
  return {
130
136
  loading: loading.value,
@@ -148,42 +154,7 @@ watch(() => props.companyProps.modelValue, value => {
148
154
  companiesModel.value = value
149
155
  }, { immediate: true })
150
156
 
151
- // composable
152
- function useAvatarNotifications () {
153
- const hasAnimated = ref(false)
154
-
155
- watch(() => unreadNotificationsCount.value, () => {
156
- hasAnimated.value = true
157
-
158
- setTimeout(() => {
159
- hasAnimated.value = false
160
- }, 1000)
161
- })
162
-
163
- const avatarNotificationCountProps = computed(() => {
164
- const classes = [
165
- 'qas-app-user__notification-avatar',
166
- 'animated',
167
- {
168
- rubberBand: hasAnimated.value
169
- }
170
- ]
171
-
172
- return {
173
- class: classes,
174
- color: 'red-14',
175
- size: 'xs',
176
- title: unreadNotificationsToString.value,
177
- useCropTitle: false
178
- }
179
- })
180
-
181
- return {
182
- avatarNotificationCountProps
183
- }
184
- }
185
-
186
- // métodos
157
+ // functions
187
158
  function signOut () {
188
159
  emit('sign-out')
189
160
  }
@@ -194,7 +165,10 @@ async function setCompanies (value) {
194
165
  loading.value = true
195
166
 
196
167
  try {
197
- await axios.patch('users/me', { companies: value })
168
+ await axios.patch('users/me', {
169
+ [IS_ME_VERSION_2 ? 'currentMainCompany' : 'companies']: value
170
+ })
171
+
198
172
  setTimeout(() => location.reload(), 1500)
199
173
 
200
174
  NotifySuccess('Vínculo alterado com sucesso.')
@@ -224,6 +198,41 @@ function onMenuHide () {
224
198
  function toggleNotificationsDrawer () {
225
199
  emit('toggle-notifications')
226
200
  }
201
+
202
+ // composables definitions
203
+ function useAvatarNotifications () {
204
+ const hasAnimated = ref(false)
205
+
206
+ watch(() => unreadNotificationsCount.value, () => {
207
+ hasAnimated.value = true
208
+
209
+ setTimeout(() => {
210
+ hasAnimated.value = false
211
+ }, 1000)
212
+ })
213
+
214
+ const avatarNotificationCountProps = computed(() => {
215
+ const classes = [
216
+ 'qas-app-user__notification-avatar',
217
+ 'animated',
218
+ {
219
+ rubberBand: hasAnimated.value
220
+ }
221
+ ]
222
+
223
+ return {
224
+ class: classes,
225
+ color: 'red-14',
226
+ size: 'xs',
227
+ title: unreadNotificationsToString.value,
228
+ useCropTitle: false
229
+ }
230
+ })
231
+
232
+ return {
233
+ avatarNotificationCountProps
234
+ }
235
+ }
227
236
  </script>
228
237
 
229
238
  <style lang="scss">
@@ -46,8 +46,9 @@ import zoomPlugin from 'chartjs-plugin-zoom'
46
46
  import chartDataLabels from 'chartjs-plugin-datalabels'
47
47
 
48
48
  // Outras importações
49
- import { extend } from 'quasar'
50
49
  import { filterListByHandle } from '../../helpers'
50
+
51
+ import { extend, is } from 'quasar'
51
52
  import { getAction } from '@bildvitta/store-adapter'
52
53
 
53
54
  const ChartTypes = {
@@ -131,6 +132,11 @@ export default {
131
132
  type: String
132
133
  },
133
134
 
135
+ urlQueryList: {
136
+ default: () => (['company']),
137
+ type: Array
138
+ },
139
+
134
140
  useBox: {
135
141
  type: Boolean,
136
142
  default: true
@@ -332,6 +338,10 @@ export default {
332
338
  ...(this.useBox && { ...this.boxProps })
333
339
  }
334
340
  }
341
+ },
342
+
343
+ queryFromURL () {
344
+ return this.getQueryFromURL(this.$route.query)
335
345
  }
336
346
  },
337
347
 
@@ -342,6 +352,10 @@ export default {
342
352
 
343
353
  isFetching (value) {
344
354
  this.$emit('update:fetching', value)
355
+ },
356
+
357
+ $route (to, from) {
358
+ this.onRouteChange(to, from)
345
359
  }
346
360
  },
347
361
 
@@ -357,9 +371,10 @@ export default {
357
371
  methods: {
358
372
  handleFetchData () {
359
373
  const hasBeforeFetch = typeof this.beforeFetch === 'function'
374
+
360
375
  const payload = {
361
376
  url: this.url,
362
- filters: this.filters
377
+ filters: { ...this.filters, ...this.queryFromURL }
363
378
  }
364
379
 
365
380
  if (hasBeforeFetch && !this.cancelBeforeFetch) {
@@ -435,6 +450,44 @@ export default {
435
450
  ...this.defaultChartItems,
436
451
  ...this.elementsChartItems[this.type]
437
452
  )
453
+ },
454
+
455
+ onRouteChange (to, from) {
456
+ const { query } = to
457
+
458
+ const isSameRoute = to.name === from.name
459
+ const hasQueryInURLQueryList = this.urlQueryList.some(urlQuery => query[urlQuery])
460
+
461
+ /**
462
+ * feito com função para evitar um deepEqual desnecessário, uma vez que
463
+ * caso "isSameRoute" seja falso, ou "hasQueryInURLQueryList" seja falso,
464
+ * não é necessário fazer a comparação.
465
+ */
466
+ const hasQueryChanged = () => !is.deepEqual(this.queryFromURL, this.getQueryFromURL(from.query))
467
+
468
+ /**
469
+ * Verifica se a rota atual é a mesma, se há uma query na URL que corresponde à queryList,
470
+ * e se a query correspondente à queryList foi alterada. Se todas essas condições forem
471
+ * verdadeiras, faz a requisição de dados.
472
+ */
473
+ if (isSameRoute && hasQueryInURLQueryList && hasQueryChanged()) this.handleFetchData()
474
+ },
475
+
476
+ /**
477
+ * "urlQueryList" é uma lista de query que o componente deve pegar da URL.
478
+ */
479
+ getQueryFromURL (query = {}) {
480
+ const newQuery = {}
481
+
482
+ this.urlQueryList.forEach(urlQuery => {
483
+ const queryValue = query[urlQuery]
484
+
485
+ if (queryValue) {
486
+ newQuery[urlQuery] = queryValue
487
+ }
488
+ })
489
+
490
+ return newQuery
438
491
  }
439
492
  }
440
493
  }
@@ -70,6 +70,12 @@ props:
70
70
  desc: Envia como parâmetro para a action "fetchList" do modulo correspondente a "entity".
71
71
  type: String
72
72
 
73
+ url-query-list:
74
+ desc: Lista de query que serão recuperadas da URL e enviadas como parâmetro para a action "fetchList" do modulo correspondente a "entity".
75
+ type: Array
76
+ default:
77
+ - company
78
+
73
79
  use-box:
74
80
  desc: Controla se o componente vai ter o QasBox englobando ou não.
75
81
  default: true
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div ref="expansionItem" class="full-width qas-expansion-item" :class="classes" v-bind="expansionProps.parent">
3
- <component :is="component.is" class="qas-expansion-item__box" v-bind="boxProps">
3
+ <qas-box class="qas-expansion-item__box" v-bind="boxProps">
4
4
  <q-expansion-item v-model="modelValue" v-bind="expansionProps.item" header-class="text-bold q-mt-sm q-pa-none qas-expansion-item__header" @show="setShowContent">
5
5
  <template #header>
6
6
  <div class="full-width justify-between no-wrap row">
@@ -28,17 +28,13 @@
28
28
  </div>
29
29
  </template>
30
30
 
31
- <q-separator v-if="hasHeaderSeparator" class="q-my-md" />
32
-
33
31
  <div :class="contentClasses">
34
32
  <slot v-if="showContent" name="content">
35
33
  <qas-grid-generator v-if="hasGridGenerator" use-inline v-bind="gridGeneratorProps" />
36
34
  </slot>
37
35
  </div>
38
-
39
- <q-separator v-if="hasBottomSeparator" class="q-mt-md" />
40
36
  </q-expansion-item>
41
- </component>
37
+ </qas-box>
42
38
 
43
39
  <div v-if="hasError" class="q-pt-sm qas-expansion-item__error-message text-caption text-negative">
44
40
  {{ props.errorMessage }}
@@ -49,7 +45,7 @@
49
45
  <script setup>
50
46
  import QasBox from '../box/QasBox.vue'
51
47
 
52
- import { computed, provide, inject, onMounted, ref, useAttrs } from 'vue'
48
+ import { computed, provide, inject, ref, useAttrs } from 'vue'
53
49
 
54
50
  defineOptions({
55
51
  name: 'QasExpansionItem',
@@ -97,11 +93,6 @@ const props = defineProps({
97
93
  maxContentHeight: {
98
94
  type: String,
99
95
  default: ''
100
- },
101
-
102
- useHeaderSeparator: {
103
- type: Boolean,
104
- default: undefined
105
96
  }
106
97
  })
107
98
 
@@ -116,37 +107,18 @@ const slots = defineSlots()
116
107
 
117
108
  // refs
118
109
  const expansionItem = ref(null)
119
- const hasNextSibling = ref(false)
120
110
  const showContent = ref(false)
121
111
 
122
- onMounted(setHasNextSibling)
123
-
124
112
  // constants
125
113
  const isNestedExpansionItem = inject('isExpansionItem', false)
126
114
  const isNestedBox = inject('isBox', false)
127
115
 
128
- const component = {
129
- is: isNestedExpansionItem ? 'div' : QasBox
130
- }
131
-
132
116
  // computed
133
117
  const hasBadges = computed(() => !!props.badges.length)
134
118
  const hasError = computed(() => props.error || !!props.errorMessage)
135
119
  const hasGridGenerator = computed(() => !!Object.keys(props.gridGeneratorProps).length)
136
- const hasBottomSeparator = computed(() => isNestedExpansionItem && hasNextSibling.value)
137
120
  const hasHeaderBottom = computed(() => !!slots['header-bottom'])
138
121
 
139
- /**
140
- * Verifica se o componente deve adicionar um separador no header.
141
- *
142
- * - Se a propriedade useHeaderSeparator for true, retorna separador.
143
- * - Se a propriedade useHeaderSeparator for undefined, retorna separador apenas se não for um componente aninhado.
144
- * - Se a propriedade useHeaderSeparator for false, não retorna separador.
145
- */
146
- const hasHeaderSeparator = computed(() => {
147
- return typeof props.useHeaderSeparator === 'undefined' ? !isNestedExpansionItem : props.useHeaderSeparator
148
- })
149
-
150
122
  const classes = computed(() => {
151
123
  return {
152
124
  'qas-expansion-item--error': hasError.value,
@@ -157,8 +129,7 @@ const classes = computed(() => {
157
129
 
158
130
  const contentClasses = computed(() => {
159
131
  return {
160
- 'q-mt-sm': isNestedExpansionItem,
161
- 'q-mt-md': !isNestedExpansionItem && !props.useHeaderSeparator,
132
+ 'q-mt-md': true,
162
133
  'qas-expansion-item__content overflow-auto': !!props.maxContentHeight
163
134
  }
164
135
  })
@@ -196,10 +167,10 @@ const expansionProps = computed(() => {
196
167
 
197
168
  const boxProps = computed(() => {
198
169
  /**
199
- * Caso o QasExpansionItem estiver dentro de um QasBox e não for um QasExpansionItem
170
+ * Caso o QasExpansionItem estiver dentro de um QasBox ou for um QasExpansionItem
200
171
  * dentro de outro QasExpansionItem, o componente terá uma borda.
201
172
  */
202
- const isBoxed = isNestedBox && !isNestedExpansionItem
173
+ const isBoxed = isNestedBox || isNestedExpansionItem
203
174
 
204
175
  if (!isBoxed) return {}
205
176
 
@@ -212,20 +183,6 @@ const boxProps = computed(() => {
212
183
  const isDisabled = computed(() => props.disable || props.disableButton)
213
184
 
214
185
  // functions
215
-
216
- /**
217
- * Caso o componente esteja dentro de um QasExpansionItem, verifica se existe um próximo irmão
218
- * para adicionar um separador.
219
- */
220
- function setHasNextSibling () {
221
- if (!isNestedExpansionItem) return
222
-
223
- const hasTextContentSibling = !!expansionItem.value.nextSibling?.textContent?.trim?.()
224
- const hasElementSibling = !!expansionItem.value.nextElementSibling
225
-
226
- hasNextSibling.value = hasElementSibling || hasTextContentSibling
227
- }
228
-
229
186
  function setShowContent () {
230
187
  showContent.value = true
231
188
  }
@@ -49,11 +49,6 @@ props:
49
49
  desc: Propriedades que serão repassadas para o QasGridGenerator.
50
50
  type: Object
51
51
 
52
- use-header-separator:
53
- desc: Propriedade para forçar o QSeparator no header.
54
- type: Boolean
55
- default: undefined
56
-
57
52
  slots:
58
53
  header:
59
54
  desc: Slot para substituir o conteúdo do header (não inclui o botão dropdown).
@@ -43,10 +43,12 @@
43
43
  <script>
44
44
  import QasFilters from '../filters/QasFilters.vue'
45
45
  import QasPagination from '../pagination/QasPagination.vue'
46
+
47
+ import { viewMixin, contextMixin } from '../../mixins'
48
+
46
49
  import debug from 'debug'
47
50
  import { extend } from 'quasar'
48
51
  import { getState, getAction } from '@bildvitta/store-adapter'
49
- import { viewMixin, contextMixin } from '../../mixins'
50
52
  import { computed } from 'vue'
51
53
 
52
54
  const log = debug('asteroid-ui:qas-list-view')
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <qas-select v-model="internalModel" :label="props.label" :options="props.options" use-filter-mode @update:model-value="onUpdateModel" />
3
+ </template>
4
+
5
+ <script setup>
6
+ import useDefaultFilters from '../../composables/use-default-filters'
7
+
8
+ import { extend } from 'quasar'
9
+ import { watch, ref, nextTick } from 'vue'
10
+ import { useRouter, useRoute } from 'vue-router'
11
+
12
+ defineOptions({ name: 'QasSelectFilter' })
13
+
14
+ const props = defineProps({
15
+ label: {
16
+ type: String,
17
+ default: 'Selecione uma empresa vinculada'
18
+ },
19
+
20
+ name: {
21
+ type: String,
22
+ default: 'company'
23
+ },
24
+
25
+ options: {
26
+ type: Array,
27
+ default: () => []
28
+ }
29
+ })
30
+
31
+ // composables
32
+ const router = useRouter()
33
+ const route = useRoute()
34
+ const { setFilterQuery, triggerDefaultFiltersChange, filterQuery } = useDefaultFilters()
35
+
36
+ // models
37
+ const model = defineModel({ type: String, default: '' })
38
+
39
+ // refs
40
+ const internalModel = ref(route.query[props.name] || model.value)
41
+
42
+ // watch
43
+ watch(() => route.query[props.name], setModels)
44
+
45
+ // functions
46
+ /**
47
+ * Adiciona o valor na URL, atualizando a rota, isto vai fazer com que o componente
48
+ * seja atualizado com o novo valor, pois cai no watch que atualiza o model interno e externo.
49
+ */
50
+ function onUpdateModel (value) {
51
+ const { ...query } = route.query
52
+
53
+ router.push({ query: { ...query, [props.name]: value } })
54
+ }
55
+
56
+ function setModels (value) {
57
+ model.value = value
58
+ internalModel.value = value
59
+
60
+ const oldFilters = extend(true, {}, filterQuery.value)
61
+
62
+ setFilterQuery(value, props.name)
63
+ nextTick(() => triggerDefaultFiltersChange(filterQuery.value, oldFilters))
64
+ }
65
+ </script>
@@ -0,0 +1,36 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Componente para gerar dinamicamente checkbox agrupados.
5
+
6
+ props:
7
+ inline:
8
+ desc: Controla se o componente vai aparece em linha ou em bloco.
9
+ default: true
10
+ type: Boolean
11
+
12
+ label:
13
+ desc: Label utilizada em casos de ser checkbox-group.
14
+ default: ''
15
+ type: String
16
+
17
+ model-value:
18
+ desc: Model do componente, usado para v-model.
19
+ default: []
20
+ type: Array
21
+ examples: [v-model"value"]
22
+ model: true
23
+
24
+ options:
25
+ desc: Opções para gerar os checkbox.
26
+ default: []
27
+ type: Array
28
+
29
+ events:
30
+ '@update:model-value -> function(value)':
31
+ desc: Dispara quando o model-value altera, também usado para v-model.
32
+ params:
33
+ value:
34
+ desc: Novo valor do model.
35
+ default: []
36
+ type: Array
@@ -1,8 +1,10 @@
1
1
  export { default as useContext } from './use-context.js'
2
+ export { default as useDefaultFilters } from './use-default-filters.js'
2
3
  export { default as useForm } from './use-form.js'
3
4
  export { default as useHistory } from './use-history.js'
5
+ export { default as useNotifications } from './use-notifications.js'
4
6
  export { default as useQueryCache } from './use-query-cache.js'
5
7
  export { default as useScreen } from './use-screen.js'
6
- export { default as useNotifications } from './use-notifications.js'
7
8
 
8
9
  export * from './use-notifications.js'
10
+ export * from './use-default-filters.js'
@@ -0,0 +1,106 @@
1
+ import { computed, ref } from 'vue'
2
+ import { LocalStorage, is } from 'quasar'
3
+
4
+ const filterQuery = ref({})
5
+
6
+ const defaultFiltersHooks = {
7
+ defaultFiltersChange: []
8
+ }
9
+
10
+ /**
11
+ * Define os filtros padrão antes de entrar na rota.
12
+ *
13
+ * Prioridade de aplicação dos filtros:
14
+ * 1 - Se o filtro estiver salvo no estado global, então utiliza ele.
15
+ * 2 - Se o filtro já estiver na query, então utiliza ele.
16
+ * 3 - Se o filtro já estiver salvo no LocalStorage, então utiliza ele.
17
+ *
18
+ * @param {Object} to - Rota de destino.
19
+ * @param {Object} _from - Rota de origem.
20
+ * @param {Function} next - Função de redirecionamento.
21
+ * @param {Array} queryList='company' - Lista de filtros a serem aplicados.
22
+ */
23
+ export function setDefaultFiltersBeforeEnter (to, _from, next, queryList = ['company']) {
24
+ const { getDefaultFiltersFromStorage, setFilterQuery } = useDefaultFilters()
25
+
26
+ const { query } = to
27
+ const newQuery = { ...query }
28
+
29
+ // recupera os filtros padrão do LocalStorage
30
+ const defaultFiltersFromStorage = getDefaultFiltersFromStorage()
31
+
32
+ queryList.forEach(name => {
33
+ // 1. se o filtro já estiver salvo no estado, então utiliza ele
34
+ if (filterQuery.value[name]) {
35
+ newQuery[name] = filterQuery.value[name]
36
+ return
37
+ }
38
+
39
+ // 2. se o filtro já estiver na query, então utiliza ele
40
+ if (query[name]) {
41
+ setFilterQuery(query[name], name)
42
+
43
+ return
44
+ }
45
+
46
+ const storedFilter = defaultFiltersFromStorage[name]
47
+
48
+ // 3. se o filtro já estiver salvo no LocalStorage, então utiliza ele
49
+ if (storedFilter) {
50
+ setFilterQuery(storedFilter, name)
51
+ newQuery[name] = storedFilter
52
+ }
53
+ })
54
+
55
+ /**
56
+ * Verifica se houve mudanças na query antes de redirecionar, sem essa validação
57
+ * o redirecionamento ocorre mesmo que a query seja a mesma, gerando loop infinito.
58
+ */
59
+ if (!is.deepEqual(newQuery, query)) return next({ ...to, query: newQuery, replace: true })
60
+
61
+ next()
62
+ }
63
+
64
+ /**
65
+ * Este composable recupera os filtros default salvos no LocalStorage na chave 'defaultFilters'.
66
+ */
67
+ export default function useDefaultFilters () {
68
+ const hasFilterQuery = computed(() => !!Object.keys(filterQuery.value).length)
69
+
70
+ function setFilterQuery (query, name = 'company') {
71
+ filterQuery.value[name] = query
72
+ }
73
+
74
+ function getDefaultFiltersFromStorage () {
75
+ const defaultFiltersFromStorage = LocalStorage.getItem('defaultFilters') || {}
76
+
77
+ return defaultFiltersFromStorage
78
+ }
79
+
80
+ function triggerDefaultFiltersChange (newFilters, oldFilters) {
81
+ // necessário verificar se houve mudanças antes de disparar o evento para não duplicar.
82
+ if (is.deepEqual(newFilters, oldFilters)) return
83
+
84
+ defaultFiltersHooks.defaultFiltersChange.forEach(hook => hook(newFilters, oldFilters))
85
+ }
86
+
87
+ function onDefaultFiltersChange (callback) {
88
+ defaultFiltersHooks.defaultFiltersChange.push(callback)
89
+ }
90
+
91
+ function removeOnDefaultFiltersChange (callback) {
92
+ const index = defaultFiltersHooks.defaultFiltersChange.indexOf(callback)
93
+
94
+ if (~index) defaultFiltersHooks.defaultFiltersChange.splice(index, 1)
95
+ }
96
+
97
+ return {
98
+ filterQuery,
99
+ hasFilterQuery,
100
+ getDefaultFiltersFromStorage,
101
+ onDefaultFiltersChange,
102
+ removeOnDefaultFiltersChange,
103
+ setFilterQuery,
104
+ triggerDefaultFiltersChange
105
+ }
106
+ }
@@ -19,7 +19,7 @@ export default function () {
19
19
  }
20
20
 
21
21
  function findOne (key, filter) {
22
- return cachedFilters[key][filter]
22
+ return cachedFilters[key]?.[filter]
23
23
  }
24
24
 
25
25
  function findAll (key) {
package/src/vue-plugin.js CHANGED
@@ -14,6 +14,7 @@ import QasBtnDropdown from './components/btn-dropdown/QasBtnDropdown.vue'
14
14
  import QasCard from './components/card/QasCard.vue'
15
15
  import QasCardImage from './components/card-image/QasCardImage.vue'
16
16
  import QasCheckbox from './components/checkbox/QasCheckbox.vue'
17
+ import QasSelectFilter from './components/select-filter/QasSelectFilter.vue'
17
18
  import QasCopy from './components/copy/QasCopy.vue'
18
19
  import QasDate from './components/date/QasDate.vue'
19
20
  import QasDateTimeInput from './components/date-time-input/QasDateTimeInput.vue'
@@ -112,6 +113,7 @@ async function install (app) {
112
113
  app.component('QasCard', QasCard)
113
114
  app.component('QasCardImage', QasCardImage)
114
115
  app.component('QasCheckbox', QasCheckbox)
116
+ app.component('QasSelectFilter', QasSelectFilter)
115
117
  app.component('QasCopy', QasCopy)
116
118
  app.component('QasDate', QasDate)
117
119
  app.component('QasDateTimeInput', QasDateTimeInput)
@@ -213,6 +215,7 @@ export {
213
215
  QasCard,
214
216
  QasCheckbox,
215
217
  QasCopy,
218
+ QasSelectFilter,
216
219
  QasDate,
217
220
  QasDateTimeInput,
218
221
  QasDebugger,