@bildvitta/quasar-ui-asteroid 2.23.0-beta.5 → 2.23.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,6 +1,6 @@
1
1
  {
2
2
  "name": "@bildvitta/quasar-ui-asteroid",
3
- "version": "2.23.0-beta.5",
3
+ "version": "2.23.0",
4
4
  "description": "",
5
5
  "main": "./src/index.js",
6
6
  "scripts": {
@@ -33,6 +33,10 @@ export default {
33
33
 
34
34
  argTypes: {
35
35
  // Props
36
+ beforeFetch: {
37
+ description: 'Function to be called before fetching data.'
38
+ },
39
+
36
40
  cancelButton: {
37
41
  description: 'Cancel button label.'
38
42
  },
@@ -80,6 +84,10 @@ export default {
80
84
  description: 'Submit button label.'
81
85
  },
82
86
 
87
+ beforeSubmit: {
88
+ description: 'Function to be called before submit.'
89
+ },
90
+
83
91
  url: {
84
92
  control: null,
85
93
  description: 'Ignore entity and specify another endpoint.'
@@ -4,7 +4,7 @@
4
4
  <slot :errors="errors" :fields="fields" :metadata="metadata" name="header" />
5
5
  </header>
6
6
 
7
- <q-form ref="form" @submit="submit">
7
+ <q-form ref="form" @submit="submitHandler">
8
8
  <slot :errors="errors" :fields="fields" :metadata="metadata" />
9
9
 
10
10
  <slot v-if="!readOnly" :errors="errors" :fields="fields" :metadata="metadata" name="actions">
@@ -13,7 +13,7 @@
13
13
  <qas-btn v-close-popup="dialog" class="full-width" :data-cy="`btnCancel-${entity}`" :disable="isCancelButtonDisabled" :label="cancelButton" outline type="button" @click="cancel" />
14
14
  </div>
15
15
  <div class="col-12 col-sm-2" :class="saveButtonClass">
16
- <qas-btn class="full-width" :data-cy="`btnSave-${entity}`" :disable="disable" :label="submitButton" :loading="isSubmiting" type="submit" />
16
+ <qas-btn class="full-width" :data-cy="`btnSave-${entity}`" :disable="disable" :label="submitButton" :loading="isSubmitting" type="submit" />
17
17
  </div>
18
18
  </div>
19
19
  </slot>
@@ -95,6 +95,11 @@ export default {
95
95
  type: String
96
96
  },
97
97
 
98
+ beforeSubmit: {
99
+ default: null,
100
+ type: Function
101
+ },
102
+
98
103
  value: {
99
104
  default: () => ({}),
100
105
  type: Object
@@ -105,7 +110,7 @@ export default {
105
110
  return {
106
111
  cachedResult: {},
107
112
  hasResult: false,
108
- isSubmiting: false,
113
+ isSubmitting: false,
109
114
  showDialog: false,
110
115
 
111
116
  dialogConfig: {
@@ -181,7 +186,7 @@ export default {
181
186
  },
182
187
 
183
188
  created () {
184
- this.fetch()
189
+ this.fetchHandler({ form: true, id: this.id, url: this.fetchURL }, this.fetchSingle)
185
190
  },
186
191
 
187
192
  methods: {
@@ -203,14 +208,18 @@ export default {
203
208
  }
204
209
  },
205
210
 
206
- async fetch (params) {
211
+ async fetchSingle (externalPayload = {}) {
207
212
  this.isFetching = true
208
213
 
209
- try {
210
- const response = await this.$store.dispatch(
211
- `${this.entity}/fetchSingle`, { form: true, id: this.id, params, url: this.fetchURL }
212
- )
214
+ const payload = {
215
+ form: true,
216
+ id: this.id,
217
+ url: this.fetchURL,
218
+ ...externalPayload
219
+ }
213
220
 
221
+ try {
222
+ const response = await this.$store.dispatch(`${this.entity}/fetchSingle`, payload)
214
223
  const { errors, fields, metadata, result } = response.data
215
224
 
216
225
  this.setErrors(errors)
@@ -264,22 +273,33 @@ export default {
264
273
  this.showDialog = true
265
274
  },
266
275
 
267
- async submit (event) {
276
+ submitHandler (event) {
268
277
  if (event) {
269
278
  event.preventDefault()
270
279
  }
271
280
 
272
- if (this.disable || this.readyOnly) {
281
+ const hasBeforeSubmit = typeof this.beforeSubmit === 'function'
282
+
283
+ if (hasBeforeSubmit) {
284
+ return this.beforeSubmit({
285
+ params: { id: this.id, payload: this.value, url: this.url },
286
+ resolve: payload => this.submit(payload)
287
+ })
288
+ }
289
+
290
+ this.submit()
291
+ },
292
+
293
+ async submit (externalPayload = {}) {
294
+ if (this.disable) {
273
295
  return null
274
296
  }
275
297
 
276
- this.isSubmiting = true
298
+ this.isSubmitting = true
277
299
 
278
300
  try {
279
- const response = await this.$store.dispatch(
280
- `${this.entity}/${this.mode}`,
281
- { id: this.id, payload: this.value, url: this.url }
282
- )
301
+ const payload = { id: this.id, payload: this.value, url: this.url, ...externalPayload }
302
+ const response = await this.$store.dispatch(`${this.entity}/${this.mode}`, payload)
283
303
 
284
304
  if (this.showDialogOnUnsavedChanges) {
285
305
  this.cachedResult = cloneDeep(this.value)
@@ -301,7 +321,7 @@ export default {
301
321
 
302
322
  this.$emit('submit-error', error)
303
323
  } finally {
304
- this.isSubmiting = false
324
+ this.isSubmitting = false
305
325
  }
306
326
  }
307
327
  }
@@ -41,6 +41,10 @@ export default {
41
41
 
42
42
  argTypes: {
43
43
  // Props
44
+ beforeFetch: {
45
+ description: 'Function to be called before fetching data.'
46
+ },
47
+
44
48
  dialog: {
45
49
  description: 'Use when the component is inside a dialog.'
46
50
  },
@@ -67,6 +71,10 @@ export default {
67
71
  description: 'Ignore entity and specify another endpoint.'
68
72
  },
69
73
 
74
+ useResultsAreaOnly: {
75
+ description: 'Controls whether results will always be displayed regardless of there are no results to be displayed.'
76
+ },
77
+
70
78
  // Events
71
79
  'fetch-error': {
72
80
  description: 'Fires when occur an error fetching value.',
@@ -10,7 +10,7 @@
10
10
  </slot>
11
11
 
12
12
  <main class="relative-position">
13
- <div v-if="hasResults">
13
+ <div v-if="showResults">
14
14
  <slot :fields="fields" :metadata="metadata" :results="results" />
15
15
  </div>
16
16
 
@@ -67,6 +67,10 @@ export default {
67
67
  filtersProps: {
68
68
  default: () => ({}),
69
69
  type: Object
70
+ },
71
+
72
+ useResultsAreaOnly: {
73
+ type: Boolean
70
74
  }
71
75
  },
72
76
 
@@ -77,11 +81,6 @@ export default {
77
81
  },
78
82
 
79
83
  computed: {
80
- context () {
81
- const { limit, ordering, page, search, ...filters } = this.$route.query
82
- return { filters, limit, ordering, page: page ? parseInt(page) : 1, search }
83
- },
84
-
85
84
  hasHeaderSlot () {
86
85
  return !!(this.$slots.header || this.$scopedSlots.header)
87
86
  },
@@ -98,6 +97,10 @@ export default {
98
97
  return this.$store.getters[`${this.entity}/list`]
99
98
  },
100
99
 
100
+ showResults () {
101
+ return this.hasResults || (this.useResultsAreaOnly && !this.isFetching)
102
+ },
103
+
101
104
  totalPages () {
102
105
  return this.$store.getters[`${this.entity}/totalPages`]
103
106
  }
@@ -105,13 +108,13 @@ export default {
105
108
 
106
109
  watch: {
107
110
  $route () {
108
- this.fetchList()
111
+ this.onFetchHandler()
109
112
  this.setCurrentPage()
110
113
  }
111
114
  },
112
115
 
113
116
  created () {
114
- this.fetchList()
117
+ this.onFetchHandler()
115
118
  this.setCurrentPage()
116
119
  },
117
120
 
@@ -148,13 +151,17 @@ export default {
148
151
  },
149
152
 
150
153
  async refresh (done) {
151
- await this.fetchList()
154
+ await this.onFetchHandler()
152
155
 
153
156
  if (typeof done === 'function') {
154
157
  done()
155
158
  }
156
159
  },
157
160
 
161
+ async onFetchHandler () {
162
+ await this.fetchHandler({ ...this.context, url: this.url }, this.fetchList)
163
+ },
164
+
158
165
  setCurrentPage () {
159
166
  this.page = parseInt(this.$route.query.page || 1)
160
167
  }
@@ -91,7 +91,7 @@ export default {
91
91
 
92
92
  computed: {
93
93
  contentClasses () {
94
- return ['overflow-auto', 'q-mt-xs', 'relative-position', this.$_virtualScrollClassName]
94
+ return ['overflow-auto', 'q-mt-xs', 'relative-position']
95
95
  },
96
96
 
97
97
  contentStyle () {
@@ -13,15 +13,10 @@
13
13
  </template>
14
14
 
15
15
  <template #no-option>
16
- <slot name="no-option">
16
+ <slot v-if="!isLoading" name="no-option">
17
17
  <q-item>
18
18
  <q-item-section class="text-grey">
19
- <template v-if="isLoading">
20
- Buscando opções de {{ label }}...
21
- </template>
22
- <template v-else>
23
- {{ noOptionLabel }}
24
- </template>
19
+ {{ noOptionLabel }}
25
20
  </q-item-section>
26
21
  </q-item>
27
22
  </slot>
@@ -98,10 +93,6 @@ export default {
98
93
  }
99
94
  },
100
95
 
101
- label () {
102
- return this.$attrs.label || ''
103
- },
104
-
105
96
  listeners () {
106
97
  const { input, ...events } = this.$listeners
107
98
 
@@ -148,19 +139,23 @@ export default {
148
139
  return this.hasFetchError || this.$attrs.error
149
140
  },
150
141
 
142
+ hasLoading () {
143
+ return this.isLoading || this.$attrs.loading
144
+ },
145
+
151
146
  attributes () {
152
147
  return {
148
+ bottomSlots: true,
153
149
  emitValue: true,
154
150
  mapOptions: true,
155
151
  outlined: true,
156
152
  clearable: this.isSearchable,
157
- loading: this.isLoading,
158
153
  inputDebounce: this.useLazyLoading ? 500 : 0,
159
- popupContentClass: this.$_virtualScrollClassName,
160
154
  ...this.$attrs,
161
155
  options: this.filteredOptions,
162
156
  useInput: this.isSearchable,
163
- error: this.hasError
157
+ error: this.hasError,
158
+ loading: this.hasLoading
164
159
  }
165
160
  }
166
161
  },
@@ -189,15 +184,21 @@ export default {
189
184
  const Fuse = (await import('fuse.js')).default
190
185
  this.fuse = new Fuse(this.defaultOptions, this.defaultFuseOptions)
191
186
  }
187
+
188
+ this.useLazyLoading && this.$_setFetchOptions('')
192
189
  },
193
190
 
194
191
  methods: {
195
- onFilter (value, update) {
196
- update(() => {
197
- if (this.useLazyLoading && value !== this.search) return this.$_filterOptionsByStore(value)
192
+ async onFilter (value, update) {
193
+ if (this.useLazyLoading && value !== this.search) {
194
+ await this.$_filterOptionsByStore(value)
195
+ }
196
+
197
+ if (!this.useLazyLoading && this.searchable) {
198
+ this.filterOptionsByFuse(value)
199
+ }
198
200
 
199
- if (!this.useLazyLoading && this.searchable) this.filterOptionsByFuse(value)
200
- })
201
+ update()
201
202
  },
202
203
 
203
204
  filterOptionsByFuse (value) {
@@ -33,6 +33,10 @@ export default {
33
33
 
34
34
  argTypes: {
35
35
  // Props
36
+ beforeFetch: {
37
+ description: 'Function to be called before fetching data.'
38
+ },
39
+
36
40
  customId: {
37
41
  control: null,
38
42
  description: 'Sets a custom id to `entity`. When not set, will use the `:id` route param.'
@@ -57,7 +57,7 @@ export default {
57
57
 
58
58
  watch: {
59
59
  $route () {
60
- this.fetchSingle()
60
+ this.onFetchHandler()
61
61
  },
62
62
 
63
63
  result (value) {
@@ -66,7 +66,7 @@ export default {
66
66
  },
67
67
 
68
68
  created () {
69
- this.fetchSingle()
69
+ this.onFetchHandler()
70
70
  },
71
71
 
72
72
  methods: {
@@ -94,6 +94,10 @@ export default {
94
94
  } finally {
95
95
  this.isFetching = false
96
96
  }
97
+ },
98
+
99
+ async onFetchHandler () {
100
+ await this.fetchHandler({ id: this.id, url: this.url }, this.fetchSingle)
97
101
  }
98
102
  }
99
103
  }
@@ -1,6 +1,5 @@
1
1
  import { decamelize } from 'humps'
2
2
  import { isEqual } from 'lodash'
3
- import { uid } from 'quasar'
4
3
 
5
4
  export default {
6
5
  props: {
@@ -32,9 +31,11 @@ export default {
32
31
  isScrolling: false,
33
32
  pagination: {
34
33
  page: 1,
35
- lastPage: null
34
+ lastPage: null,
35
+ hasCount: true,
36
+ hasNextPage: false
36
37
  },
37
- search: null
38
+ search: ''
38
39
  }
39
40
  },
40
41
 
@@ -62,11 +63,6 @@ export default {
62
63
 
63
64
  $_hasFilteredOptions () {
64
65
  return !!this.filteredOptions.length
65
- },
66
-
67
- $_virtualScrollClassName () {
68
- const id = uid()
69
- return `virtual-scroll-${id}`
70
66
  }
71
67
  },
72
68
 
@@ -75,7 +71,7 @@ export default {
75
71
  handler (value, oldValue) {
76
72
  if (isEqual(value, oldValue)) return
77
73
 
78
- this.$_resetFilter()
74
+ this.$_filterOptionsByStore('')
79
75
  this.$emit('input', '')
80
76
  }
81
77
  }
@@ -92,21 +88,22 @@ export default {
92
88
  this.search = search
93
89
  this.pagination = {
94
90
  page: 1,
95
- lastPage: null
91
+ lastPage: null,
92
+ hasCount: true,
93
+ hasNextPage: false
96
94
  }
97
95
  },
98
96
 
99
- async $_onVirtualScroll ({ index }) {
97
+ async $_onVirtualScroll ({ index, ref }) {
100
98
  const lastIndex = this.filteredOptions.length - 1
101
99
 
102
100
  if (index === lastIndex && this.$_canFetchOptions()) {
103
- const { scrollContainer, top } = this.$_getScrollContainerTop()
104
-
105
101
  await this.$_loadMoreOptions()
106
102
 
107
- setTimeout(() => {
108
- scrollContainer.scrollTo({ top })
109
- }, 100)
103
+ this.$nextTick(() => {
104
+ ref.reset()
105
+ ref.refresh(lastIndex)
106
+ })
110
107
  }
111
108
  },
112
109
 
@@ -144,11 +141,14 @@ export default {
144
141
  }
145
142
  })
146
143
 
147
- const { results, count } = data
144
+ const { results, count, hasNextPage } = data
145
+ const hasCount = count !== undefined
148
146
 
149
147
  this.pagination = {
150
148
  page: this.pagination.page + 1,
151
- lastPage: Math.ceil(count / params.limit)
149
+ lastPage: hasCount ? Math.ceil(count / params.limit) : null,
150
+ hasCount,
151
+ hasNextPage
152
152
  }
153
153
 
154
154
  this.$emit('fetch-options-success', data)
@@ -173,23 +173,12 @@ export default {
173
173
  },
174
174
 
175
175
  $_canFetchOptions () {
176
- const { lastPage, page } = this.pagination
177
- const hasMorePages = lastPage && page <= lastPage
176
+ const { lastPage, page, hasCount, hasNextPage } = this.pagination
177
+ const hasMorePages = hasCount ? lastPage && page <= lastPage : hasNextPage
178
178
 
179
179
  return hasMorePages && !this.isLoading && !this.isScrolling && this.useLazyLoading
180
180
  },
181
181
 
182
- $_getScrollContainerTop () {
183
- const scrollContainer = document.querySelector(`.${this.$_virtualScrollClassName}`)
184
- const scrollContainerHeight = scrollContainer.offsetHeight
185
- const scrollContainerTop = scrollContainer.scrollTop
186
-
187
- return {
188
- scrollContainer,
189
- top: scrollContainerTop + (scrollContainerHeight / 2)
190
- }
191
- },
192
-
193
182
  $_handleOptions (options) {
194
183
  if (this.labelKey && this.valueKey && this.renameKey) {
195
184
  return options.map(item => this.renameKey(item))
@@ -5,6 +5,11 @@ import { NotifyError } from '../plugins'
5
5
 
6
6
  export default {
7
7
  props: {
8
+ beforeFetch: {
9
+ default: null,
10
+ type: Function
11
+ },
12
+
8
13
  dialog: {
9
14
  type: Boolean
10
15
  },
@@ -22,10 +27,10 @@ export default {
22
27
 
23
28
  data () {
24
29
  return {
30
+ cancelBeforeFetch: false,
25
31
  errors: {},
26
32
  fields: {},
27
33
  metadata: {},
28
-
29
34
  isFetching: false
30
35
  }
31
36
  },
@@ -59,6 +64,22 @@ export default {
59
64
  }
60
65
  },
61
66
 
67
+ fetchHandler (payload, resolve) {
68
+ const hasBeforeFetch = typeof this.beforeFetch === 'function'
69
+
70
+ if (hasBeforeFetch && !this.cancelBeforeFetch) {
71
+ return this.beforeFetch({
72
+ payload,
73
+ resolve: payload => resolve(payload),
74
+ done: () => {
75
+ this.cancelBeforeFetch = true
76
+ }
77
+ })
78
+ }
79
+
80
+ resolve()
81
+ },
82
+
62
83
  setErrors (errors = {}) {
63
84
  this.errors = errors
64
85
  },