@bildvitta/quasar-ui-asteroid 3.11.0-beta.2 → 3.11.0-beta.4

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 (30) hide show
  1. package/package.json +1 -1
  2. package/src/components/chart-view/QasChartView.vue +383 -0
  3. package/src/components/chart-view/QasChartView.yml +87 -0
  4. package/src/components/chart-view/config/charts/bar.js +45 -0
  5. package/src/components/chart-view/config/charts/doughnut.js +43 -0
  6. package/src/components/chart-view/config/charts/index.js +9 -0
  7. package/src/components/chart-view/config/charts/line.js +57 -0
  8. package/src/components/chart-view/config/defaults/colors.js +19 -0
  9. package/src/components/chart-view/config/defaults/font.js +5 -0
  10. package/src/components/chart-view/config/defaults/index.js +2 -0
  11. package/src/components/chart-view/config/index.js +3 -0
  12. package/src/components/chart-view/config/plugins/index.js +3 -0
  13. package/src/components/chart-view/config/plugins/legend.js +9 -0
  14. package/src/components/chart-view/config/plugins/tooltip.js +15 -0
  15. package/src/components/chart-view/config/plugins/zoom.js +31 -0
  16. package/src/components/header-actions/QasHeaderActions.vue +17 -1
  17. package/src/components/header-actions/QasHeaderActions.yml +7 -1
  18. package/src/components/label/QasLabel.vue +5 -4
  19. package/src/components/search-box/QasSearchBox.vue +48 -17
  20. package/src/components/search-box/QasSearchBox.yml +8 -0
  21. package/src/components/search-input/QasSearchInput.vue +1 -24
  22. package/src/components/select-list/QasSelectList.vue +76 -48
  23. package/src/components/select-list/QasSelectList.yml +8 -39
  24. package/src/components/select-list/private/PvSelectListCheckbox.vue +31 -0
  25. package/src/css/components/field.scss +1 -1
  26. package/src/css/components/item.scss +4 -0
  27. package/src/helpers/rules.js +2 -2
  28. package/src/mixins/search-filter.js +1 -1
  29. package/src/vue-plugin/components/chart-view-component.js +16 -0
  30. package/src/vue-plugin/third-party-components-initializer.js +2 -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.11.0-beta.2",
4
+ "version": "3.11.0-beta.4",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "dist/asteroid.cjs.min.js",
@@ -0,0 +1,383 @@
1
+ <template>
2
+ <qas-header-actions v-if="hasHeaderActions" align-columns="end">
3
+ <template #left>
4
+ <div v-if="title" class="text-grey-9 text-h3">
5
+ {{ title }}
6
+ </div>
7
+
8
+ <div v-if="subtitle" class="text-body1 text-grey-8">
9
+ {{ subtitle }}
10
+ </div>
11
+ </template>
12
+
13
+ <template #right>
14
+ <qas-filters v-bind="chartFiltersProps" v-model:currentFilters="filters" />
15
+ </template>
16
+ </qas-header-actions>
17
+
18
+ <div v-bind="parentComponentProps">
19
+ <component :is="chartComponent.is" v-if="showChart" v-bind="chartComponent.props" />
20
+
21
+ <div v-else-if="!isFetching">
22
+ <slot name="empty-results">
23
+ <qas-empty-result-text />
24
+ </slot>
25
+ </div>
26
+
27
+ <q-inner-loading :showing="isFetching">
28
+ <q-spinner color="grey" size="3em" />
29
+ </q-inner-loading>
30
+ </div>
31
+ </template>
32
+
33
+ <script>
34
+ // Importações do chart.js
35
+ import {
36
+ Chart as ChartJS,
37
+ Title,
38
+ Tooltip,
39
+ Legend,
40
+ CategoryScale,
41
+ LinearScale,
42
+ BarElement,
43
+ LineElement,
44
+ PointElement,
45
+ ArcElement
46
+ } from 'chart.js'
47
+ import { Bar as BarChart, Doughnut as DoughnutChart, Line as LineChart } from 'vue-chartjs'
48
+
49
+ // Configurações padrões
50
+ import { charts, colors, font } from './config'
51
+
52
+ // Plugins
53
+ import zoomPlugin from 'chartjs-plugin-zoom'
54
+ import chartDataLabels from 'chartjs-plugin-datalabels'
55
+
56
+ // Outras importações
57
+ import { extend } from 'quasar'
58
+ import { filterListByHandle } from '../../helpers'
59
+ import { getAction } from '@bildvitta/store-adapter'
60
+
61
+ const ChartTypes = {
62
+ Bar: 'bar',
63
+ Doughnut: 'doughnut',
64
+ Line: 'line'
65
+ }
66
+
67
+ export default {
68
+ name: 'QasChartView',
69
+
70
+ components: {
71
+ BarChart,
72
+ DoughnutChart,
73
+ LineChart
74
+ },
75
+
76
+ props: {
77
+ entity: {
78
+ required: true,
79
+ type: String
80
+ },
81
+
82
+ fetching: {
83
+ type: Boolean
84
+ },
85
+
86
+ filtersProps: {
87
+ default: () => ({}),
88
+ type: Object
89
+ },
90
+
91
+ height: {
92
+ default: '380px',
93
+ type: String
94
+ },
95
+
96
+ maxDoughnutSlices: {
97
+ default: 15,
98
+ type: Number
99
+ },
100
+
101
+ options: {
102
+ default: () => ({}),
103
+ type: Object
104
+ },
105
+
106
+ subtitle: {
107
+ default: '',
108
+ type: String
109
+ },
110
+
111
+ title: {
112
+ default: '',
113
+ type: String
114
+ },
115
+
116
+ type: {
117
+ default: ChartTypes.Bar,
118
+ type: String,
119
+ validator: value => Object.values(ChartTypes).includes(value)
120
+ },
121
+
122
+ url: {
123
+ default: '',
124
+ type: String
125
+ },
126
+
127
+ useFilterButton: {
128
+ type: Boolean
129
+ }
130
+ },
131
+
132
+ emits: [
133
+ 'fetch-error',
134
+ 'fetch-success',
135
+ 'update:fetching'
136
+ ],
137
+
138
+ data () {
139
+ return {
140
+ data: [],
141
+ filters: {},
142
+ isFetched: false,
143
+ isFetching: false
144
+ }
145
+ },
146
+
147
+ computed: {
148
+ chartData () {
149
+ if (!this.data.length) {
150
+ return {
151
+ labels: [],
152
+ datasets: []
153
+ }
154
+ }
155
+
156
+ const [dataset] = this.data
157
+ const labels = this.getXAxisData(dataset.data.map(item => item.x))
158
+
159
+ const datasets = this.data.map(({ label, data }, index) => {
160
+ const backgroundColor = this.isDoughnut ? colors : colors.at(index)
161
+ const borderColor = this.isDoughnut ? 'white' : colors.at(index)
162
+ const yAxisData = []
163
+ const xAxisData = []
164
+
165
+ for (const dataKey in data) {
166
+ const item = data[dataKey]
167
+
168
+ if (item) {
169
+ yAxisData.push(item.y)
170
+ xAxisData.push(item.tooltip)
171
+ }
172
+ }
173
+
174
+ return {
175
+ backgroundColor,
176
+ borderColor,
177
+ data: this.getYAxisData(yAxisData),
178
+ label,
179
+ tooltips: this.getXAxisData(xAxisData)
180
+ }
181
+ })
182
+
183
+ return {
184
+ datasets,
185
+ labels
186
+ }
187
+ },
188
+
189
+ chartFiltersProps () {
190
+ const { entity, useFilterButton, url } = this
191
+
192
+ return {
193
+ class: 'q-mb-xs',
194
+ entity,
195
+ url,
196
+ useChip: false,
197
+ useFilterButton,
198
+ useSearch: false,
199
+ useSpacing: false,
200
+ useUpdateRoute: false,
201
+
202
+ ...this.filtersProps
203
+ }
204
+ },
205
+
206
+ chartOptions () {
207
+ const { options, type } = this
208
+
209
+ return extend(true, charts[type], options)
210
+ },
211
+
212
+ chartPlugins () {
213
+ return filterListByHandle([
214
+ {
215
+ handle: this.isDoughnut,
216
+ item: chartDataLabels
217
+ },
218
+ {
219
+ handle: this.isBar || this.isLine,
220
+ item: zoomPlugin
221
+ }
222
+ ])
223
+ },
224
+
225
+ chartType () {
226
+ const components = {
227
+ bar: 'BarChart',
228
+ doughnut: 'DoughnutChart',
229
+ line: 'LineChart'
230
+ }
231
+
232
+ return components[this.type]
233
+ },
234
+
235
+ chartComponent () {
236
+ return {
237
+ is: this.chartType,
238
+ props: {
239
+ data: this.chartData,
240
+ options: this.chartOptions,
241
+ plugins: this.chartPlugins
242
+ }
243
+ }
244
+ },
245
+
246
+ parentComponentProps () {
247
+ return {
248
+ class: 'relative-position',
249
+ style: `min-height: ${this.height}`
250
+ }
251
+ },
252
+
253
+ hasDataSets () {
254
+ return !!this.chartData?.datasets.filter(dataset => dataset.data.length)?.length
255
+ },
256
+
257
+ hasHeaderActions () {
258
+ return this.title || this.subtitle || this.useFilterButton
259
+ },
260
+
261
+ isBar () {
262
+ return this.type === 'bar'
263
+ },
264
+
265
+ isDoughnut () {
266
+ return this.type === 'doughnut'
267
+ },
268
+
269
+ isLine () {
270
+ return this.type === 'line'
271
+ },
272
+
273
+ showChart () {
274
+ return this.isFetched && this.hasDataSets
275
+ },
276
+
277
+ defaultChartItems () {
278
+ return [
279
+ CategoryScale,
280
+ LinearScale,
281
+ PointElement,
282
+ Title,
283
+ Tooltip,
284
+ Legend
285
+ ]
286
+ },
287
+
288
+ elementsChartItems () {
289
+ return {
290
+ bar: [BarElement],
291
+ doughnut: [ArcElement],
292
+ line: [LineElement]
293
+ }
294
+ }
295
+ },
296
+
297
+ watch: {
298
+ filters () {
299
+ this.fetchData()
300
+ },
301
+
302
+ isFetching (value) {
303
+ this.$emit('update:fetching', value)
304
+ }
305
+ },
306
+
307
+ created () {
308
+ this.registerChartJS()
309
+ this.fetchData()
310
+ },
311
+
312
+ unmounted () {
313
+ this.unregisterChartJS()
314
+ },
315
+
316
+ methods: {
317
+ async fetchData () {
318
+ try {
319
+ this.isFetching = true
320
+
321
+ const response = await getAction.call(this, {
322
+ entity: this.entity,
323
+ key: 'fetchList',
324
+ payload: {
325
+ url: this.url,
326
+ filters: this.filters
327
+ }
328
+ })
329
+
330
+ const { results } = response.data
331
+ this.data = results
332
+
333
+ this.$emit('fetch-success', response)
334
+ } catch (error) {
335
+ this.$qas.error('Ops… Não conseguimos acessar as informações. Por favor, tente novamente em alguns minutos.')
336
+
337
+ this.$emit('fetch-error', error)
338
+ } finally {
339
+ this.isFetching = false
340
+ this.isFetched = true
341
+ }
342
+ },
343
+
344
+ getXAxisData (data = []) {
345
+ if (this.isDoughnut && data.length > this.maxDoughnutSlices) {
346
+ data = data.slice(0, this.maxDoughnutSlices - 1)
347
+ data.push('Outros')
348
+ }
349
+
350
+ return data
351
+ },
352
+
353
+ getYAxisData (data = []) {
354
+ if (this.isDoughnut && data.length > this.maxDoughnutSlices) {
355
+ const otherSlicesValues = data
356
+ .slice(this.maxDoughnutSlices - 1)
357
+ .reduce((accumulator, currentValue) => accumulator + currentValue, 0)
358
+
359
+ data = data.slice(0, this.maxDoughnutSlices - 1)
360
+ data.push(otherSlicesValues)
361
+ }
362
+
363
+ return data
364
+ },
365
+
366
+ registerChartJS () {
367
+ ChartJS.register(
368
+ ...this.defaultChartItems,
369
+ ...this.elementsChartItems[this.type]
370
+ )
371
+
372
+ ChartJS.defaults.font = font
373
+ },
374
+
375
+ unregisterChartJS () {
376
+ ChartJS.unregister(
377
+ ...this.defaultChartItems,
378
+ ...this.elementsChartItems[this.type]
379
+ )
380
+ }
381
+ }
382
+ }
383
+ </script>
@@ -0,0 +1,87 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Componente responsável pela renderização de gráficos
5
+
6
+ props:
7
+ entity:
8
+ desc: Entidade da store, por exemplo se tiver que trabalhar com modulo de usuários, teremos o model "users" na store, que vai ser nossa "entity".
9
+ required: true
10
+ type: String
11
+
12
+ fetching:
13
+ model: true
14
+ desc: Model de fetching, utilizado para saber se o componente está fazendo fetching de dados.
15
+ type: Boolean
16
+ examples: [v-model:fetching="isFetching"]
17
+
18
+ filters-props:
19
+ desc: Repassa as props para o componente "QasFilters".
20
+ default: {}
21
+ type: Object
22
+
23
+ height:
24
+ desc: Define a altura do gráfico.
25
+ default: 380px
26
+ type: String
27
+
28
+ max-doughnut-slices:
29
+ desc: Define o máximo de fatias que o gráfico de Doughnut poderá ter. Passando o número máximo definido, o gráfico irá exibir uma fatia chamada "Outros".
30
+ default: 15
31
+ type: Number
32
+
33
+ options:
34
+ desc: Opções do chart.js que serão mescladas com as opções padrões pré definidas.
35
+ default: {}
36
+ type: Object
37
+
38
+ subtitle:
39
+ desc: Define um subtítulo para o gráfico.
40
+ type: String
41
+
42
+ title:
43
+ desc: Define um título para o gráfico.
44
+ type: String
45
+
46
+ type:
47
+ desc: Tipo de exibição do gráfico.
48
+ default: bar
49
+ type: String
50
+ examples: [bar, doughnut, line]
51
+
52
+ url:
53
+ desc: Envia como parâmetro para a action "fetchList" do modulo correspondente a "entity".
54
+ type: String
55
+
56
+ use-filter-button:
57
+ desc: Controla se o componente vai usar ou não o componente "QasFilters".
58
+ type: Boolean
59
+
60
+ slots:
61
+ default:
62
+ desc: 'Slot para ter o conteúdo principal (dentro do main).'
63
+
64
+ empty-results:
65
+ desc: 'Slot acessar quando a listagem está vazia.'
66
+
67
+ events:
68
+ '@fetch-error -> function(value)':
69
+ desc: Dispara quando a action "fetchList" cai em uma exceção.
70
+ params:
71
+ value:
72
+ desc: Retorna todos os dados "cru" respondido na exceção do fetch.
73
+ type: Object
74
+
75
+ '@fetch-success -> function(value)':
76
+ desc: Dispara quando a action "fetchList" é executada com sucesso.
77
+ params:
78
+ value:
79
+ desc: Retorna todos os dados "cru" respondido pelo fetch.
80
+ type: Object
81
+
82
+ '@update:fetching -> function(value)':
83
+ desc: Dispara logo antes da action "fetchList" e ao cair no bloco "finally", ou seja, quando começar o fetching e após terminar.
84
+ params:
85
+ value:
86
+ desc: Retorna se está ou não fazendo fetching de dados.
87
+ type: Boolean
@@ -0,0 +1,45 @@
1
+ import { textColor } from '../defaults'
2
+ import { legendPluginConfig, tooltipPluginConfig, zoomPluginConfig } from '../plugins'
3
+
4
+ export const bar = {
5
+ barPercentage: 0.7,
6
+ borderSkipped: true,
7
+ borderWidth: 2,
8
+ color: textColor,
9
+ maintainAspectRatio: false,
10
+ plugins: {
11
+ legend: legendPluginConfig,
12
+ tooltip: tooltipPluginConfig,
13
+ zoom: zoomPluginConfig
14
+ },
15
+ responsive: true,
16
+ scales: {
17
+ x: {
18
+ border: {
19
+ display: false
20
+ },
21
+
22
+ grid: {
23
+ display: false
24
+ },
25
+
26
+ ticks: {
27
+ padding: 8
28
+ }
29
+ },
30
+
31
+ y: {
32
+ border: {
33
+ display: false
34
+ },
35
+
36
+ grid: {
37
+ display: false
38
+ },
39
+
40
+ ticks: {
41
+ stepSize: 1
42
+ }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,43 @@
1
+ import { tooltipPluginConfig } from '../plugins'
2
+ import { percent } from '../../../../helpers'
3
+
4
+ function getPercentFormat (value, context) {
5
+ const total = context.dataset.data.reduce((accumulator, currentValue, index) => {
6
+ if (context.chart._hiddenIndices?.[index]) return accumulator
7
+
8
+ return accumulator + currentValue
9
+ }, 0)
10
+
11
+ const percentage = (value * 100) / total
12
+ const places = percentage % 1 ? 2 : 0
13
+
14
+ return percentage >= 5 ? percent(percentage, places) : ''
15
+ }
16
+
17
+ export const doughnut = {
18
+ borderSkipped: true,
19
+ borderWidth: 1,
20
+ maintainAspectRatio: false,
21
+ responsive: true,
22
+ plugins: {
23
+ datalabels: {
24
+ color: 'white',
25
+ font: {
26
+ weight: 600
27
+ },
28
+ formatter: getPercentFormat
29
+ },
30
+
31
+ legend: {
32
+ align: 'center',
33
+ labels: {
34
+ padding: 16,
35
+ pointStyle: 'circle',
36
+ usePointStyle: true
37
+ },
38
+ position: 'bottom'
39
+ },
40
+
41
+ tooltip: tooltipPluginConfig
42
+ }
43
+ }
@@ -0,0 +1,9 @@
1
+ import { bar } from './bar'
2
+ import { doughnut } from './doughnut'
3
+ import { line } from './line'
4
+
5
+ export default {
6
+ bar,
7
+ doughnut,
8
+ line
9
+ }
@@ -0,0 +1,57 @@
1
+ import { textColor } from '../defaults'
2
+ import { legendPluginConfig, tooltipPluginConfig, zoomPluginConfig } from '../plugins'
3
+
4
+ export const line = {
5
+ borderSkipped: true,
6
+ borderWidth: 2,
7
+ color: textColor,
8
+ maintainAspectRatio: false,
9
+ pointStyle: false,
10
+ responsive: true,
11
+
12
+ layout: {
13
+ padding: {
14
+ left: -24
15
+ }
16
+ },
17
+
18
+ plugins: {
19
+ legend: legendPluginConfig,
20
+ tooltip: tooltipPluginConfig,
21
+ zoom: zoomPluginConfig
22
+ },
23
+
24
+ scales: {
25
+ x: {
26
+ border: {
27
+ display: false
28
+ },
29
+
30
+ grid: {
31
+ color: '#E0E0E0'
32
+ },
33
+
34
+ ticks: {
35
+ padding: 8
36
+ }
37
+ },
38
+
39
+ y: {
40
+ beginAtZero: true,
41
+ offset: true,
42
+
43
+ border: {
44
+ display: false
45
+ },
46
+
47
+ grid: {
48
+ color: '#E0E0E0'
49
+ },
50
+
51
+ ticks: {
52
+ padding: 24,
53
+ stepSize: 1
54
+ }
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,19 @@
1
+ export const colors = [
2
+ '#1976D2',
3
+ '#90CAF9',
4
+ '#2962FF',
5
+ '#004198',
6
+ '#00C853',
7
+ '#651FFF',
8
+ '#26C6DA',
9
+ '#1976D2',
10
+ '#2196F3',
11
+ '#536DFE',
12
+ '#26A69A',
13
+ '#3F51B5',
14
+ '#43A047',
15
+ '#7E57C2',
16
+ '#8BC34A'
17
+ ]
18
+
19
+ export const textColor = '#616161'
@@ -0,0 +1,5 @@
1
+ export const font = {
2
+ family: 'Nunito',
3
+ size: 12,
4
+ weight: '600'
5
+ }
@@ -0,0 +1,2 @@
1
+ export * from './colors'
2
+ export * from './font'
@@ -0,0 +1,3 @@
1
+ export { default as charts } from './charts'
2
+ export * from './defaults'
3
+ export * from './plugins'
@@ -0,0 +1,3 @@
1
+ export * from './legend'
2
+ export * from './tooltip'
3
+ export * from './zoom'
@@ -0,0 +1,9 @@
1
+ export const legendPluginConfig = {
2
+ labels: {
3
+ boxHeight: 4,
4
+ boxWidth: 16,
5
+ padding: 24
6
+ },
7
+
8
+ position: 'bottom'
9
+ }
@@ -0,0 +1,15 @@
1
+ export const tooltipPluginConfig = {
2
+ backgroundColor: '#212121',
3
+ boxPadding: 4,
4
+ callbacks: {
5
+ title: function (items) {
6
+ return items.map(item => item.dataset.tooltips[item.dataIndex] || item.label)
7
+ }
8
+ },
9
+ caretSize: 0,
10
+ cornerRadius: 4,
11
+ displayColors: false,
12
+ padding: 12,
13
+ titleColor: '#fff',
14
+ titleMarginBottom: 8
15
+ }