@bildvitta/quasar-ui-asteroid 3.9.0 → 3.10.0-beta.1

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.9.0",
4
+ "version": "3.10.0-beta.1",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "dist/asteroid.cjs.min.js",
@@ -0,0 +1,412 @@
1
+ <template>
2
+ <q-date
3
+ v-model="model"
4
+ v-bind="attributes"
5
+ />
6
+ </template>
7
+
8
+ <script>
9
+ import { date } from 'quasar'
10
+ import { date as asteroidDate } from '../../helpers/filters'
11
+
12
+ const MaskOptions = {
13
+ Dash: 'YYYY-MM-DD',
14
+ Slash: 'YYYY/MM/DD',
15
+ SlashPtBR: 'DD/MM/YYYY',
16
+ SlashDateAndHourPtBR: 'DD/MM/YYYY HH:mm'
17
+ }
18
+
19
+ export default {
20
+ name: 'QasDate',
21
+
22
+ inheritAttrs: false,
23
+
24
+ props: {
25
+ events: {
26
+ type: [Array, Function],
27
+ default: () => []
28
+ },
29
+
30
+ mask: {
31
+ type: String,
32
+ default: MaskOptions.Dash,
33
+ validator: value => Object.values(MaskOptions).includes(value)
34
+ },
35
+
36
+ modelValue: {
37
+ type: [String, Array],
38
+ default: ''
39
+ },
40
+
41
+ multiple: {
42
+ type: Boolean
43
+ },
44
+
45
+ options: {
46
+ type: [Array, Function],
47
+ default: undefined
48
+ },
49
+
50
+ useIso: {
51
+ type: Boolean
52
+ },
53
+
54
+ useInactiveDates: {
55
+ default: true,
56
+ type: Boolean
57
+ },
58
+
59
+ useUnmaskEvents: {
60
+ default: true,
61
+ type: Boolean
62
+ },
63
+
64
+ useUnmaskOptions: {
65
+ default: true,
66
+ type: Boolean
67
+ },
68
+
69
+ width: {
70
+ type: String,
71
+ default: ''
72
+ }
73
+ },
74
+
75
+ emits: [
76
+ 'update:modelValue',
77
+ 'navigation'
78
+ ],
79
+
80
+ data () {
81
+ return {
82
+ currentDate: {}
83
+ }
84
+ },
85
+
86
+ computed: {
87
+ attributes () {
88
+ const {
89
+ color,
90
+ minimal,
91
+ textColor,
92
+ ...attributes
93
+ } = this.$attrs
94
+
95
+ return {
96
+ class: this.classes,
97
+ color: 'primary',
98
+ events: this.normalizedEvents,
99
+ mask: this.mask,
100
+ minimal: true,
101
+ multiple: this.multiple,
102
+ options: this.normalizedOptions,
103
+ ref: 'date',
104
+ style: this.styles,
105
+ textColor: 'white',
106
+ eventColor: this.getEventColor,
107
+
108
+ // events
109
+ onNavigation: this.onNavigation,
110
+
111
+ ...attributes
112
+ }
113
+ },
114
+
115
+ classes () {
116
+ return ['qas-date', 'shadow-2', { 'qas-date--inative': this.useInactiveDates }]
117
+ },
118
+
119
+ hasEvents () {
120
+ return !!Object.keys(this.events)?.length
121
+ },
122
+
123
+ hasModelValue () {
124
+ return this.multiple ? !!this.modelValue.length : !!this.modelValue
125
+ },
126
+
127
+ model: {
128
+ get () {
129
+ return this.modelValue
130
+ },
131
+
132
+ set (value) {
133
+ this.$emit('update:modelValue', this.useIso ? this.getISODate(value) : value)
134
+ }
135
+ },
136
+
137
+ styles () {
138
+ return {
139
+ ...(this.width && { width: this.width })
140
+ }
141
+ },
142
+
143
+ normalizedEvents () {
144
+ return this.useUnmaskEvents ? this.getUnmaskedList(this.events) : this.events
145
+ },
146
+
147
+ normalizedOptions () {
148
+ return this.useUnmaskOptions ? this.getUnmaskedList(this.options) : this.options
149
+ }
150
+ },
151
+
152
+ watch: {
153
+ events () {
154
+ if (this.useInactiveDates) this.setInactiveEvents(this.currentDate)
155
+ }
156
+ },
157
+
158
+ mounted () {
159
+ this.setNewNavigatorDisplay()
160
+
161
+ if (this.useInactiveDates) {
162
+ this.currentDate = this.getCurrentDate()
163
+ this.setInactiveEvents(this.currentDate)
164
+ }
165
+ },
166
+
167
+ methods: {
168
+ getCurrentDate () {
169
+ const modelDate = this.multiple ? this.modelValue[0] : this.modelValue
170
+ const extractedDate = this.hasModelValue ? date.extractDate(modelDate, this.mask) : new Date()
171
+
172
+ return this.getDate(extractedDate)
173
+ },
174
+
175
+ getDate (date) {
176
+ return {
177
+ year: date.getFullYear(),
178
+ month: date.getMonth() + 1,
179
+ day: date.getDate()
180
+ }
181
+ },
182
+
183
+ getEventColor (currentDate) {
184
+ if (!this.modelValue) return 'primary'
185
+
186
+ const model = this.multiple ? this.modelValue : [this.modelValue]
187
+
188
+ const extractedModel = model.filter(Boolean).map(dateItem => {
189
+ const extractedDate = date.extractDate(dateItem, this.mask)
190
+ return asteroidDate(extractedDate, 'yyyy/MM/dd')
191
+ })
192
+
193
+ return extractedModel.includes(currentDate) ? 'white' : 'primary'
194
+ },
195
+
196
+ getISODate (value) {
197
+ if (!value || (this.multiple && !value.length)) return value
198
+
199
+ return this.multiple
200
+ ? value.map(dateItem => date.extractDate(dateItem, this.mask).toISOString())
201
+ : date.extractDate(value, this.mask).toISOString()
202
+ },
203
+
204
+ getUnmaskedList (list) {
205
+ const invalidTypes = ['function', 'undefined']
206
+
207
+ return invalidTypes.includes(typeof list)
208
+ ? list
209
+ : list.map(dateItem => asteroidDate(dateItem, 'yyyy/MM/dd'))
210
+ },
211
+
212
+ async setInactiveEvents ({ year, month }) {
213
+ if (!this.hasEvents) return
214
+
215
+ const previousMonth = month - 1
216
+
217
+ const dateElement = this.$refs.date.$el
218
+ const inativeDays = dateElement.querySelectorAll('.q-date__calendar-item--fill')
219
+ const inativeList = Array.from(inativeDays)
220
+
221
+ let monthIncrement = 0
222
+
223
+ inativeList.forEach((inativeElement, index) => {
224
+ const [child] = inativeElement.children
225
+ const previousDay = inativeList?.[index - 1]?.innerText || 0
226
+ const inactiveDay = child.innerText
227
+
228
+ /*
229
+ * Se o dia anterior for 31 e o dia atual for 1, por exemplo, significa que estamos nos referindo ao próximo mês.
230
+ * Como o mês começa no mês anterior, por exemplo, em 31 de janeiro, o mês atual seria fevereiro.
231
+ * Portanto, o mês correspondente ao dia 1 seria março e, para alcançá-lo, seria necessário incrementar 2 meses.
232
+ */
233
+ if (inactiveDay < previousDay) {
234
+ monthIncrement += 2
235
+ }
236
+
237
+ const normalizedMonth = previousMonth + monthIncrement
238
+ const newDate = date.buildDate({ year, month: normalizedMonth, day: inactiveDay })
239
+ const normalizedDate = asteroidDate(newDate, 'yyyy/MM/dd')
240
+
241
+ const hasCallbackInactiveEvent = typeof this.events === 'function' && this.events(normalizedDate)
242
+ const hasArrayInactiveEvent = Array.isArray(this.events) && this.normalizedEvents.includes(normalizedDate)
243
+
244
+ if (hasArrayInactiveEvent || hasCallbackInactiveEvent) {
245
+ const divElement = document.createElement('div')
246
+ divElement.classList.add('q-date__event', 'qas-date__event-inactive')
247
+
248
+ inativeElement.appendChild(divElement)
249
+ }
250
+ })
251
+ },
252
+
253
+ setNewNavigatorDisplay () {
254
+ const dateElement = this.$refs.date.$el
255
+ const navigationElement = dateElement.querySelector('.q-date__navigation')
256
+ const navigationChildren = navigationElement?.children || []
257
+
258
+ const nodesList = Array.from(navigationChildren)
259
+
260
+ if (nodesList.length <= 2) return
261
+
262
+ const firstDivElement = document.createElement('div')
263
+ const secondDivElement = document.createElement('div')
264
+
265
+ firstDivElement.classList.add('flex')
266
+ secondDivElement.classList.add('flex')
267
+
268
+ const firstList = nodesList.slice(0, 3)
269
+ const secondList = nodesList.slice(3, 6)
270
+
271
+ const newFirstElement = navigationElement.appendChild(firstDivElement)
272
+ const newSecondElement = navigationElement.appendChild(secondDivElement)
273
+
274
+ this.$nextTick(() => firstList.forEach(node => newFirstElement.appendChild(node)))
275
+ this.$nextTick(() => secondList.forEach(node => newSecondElement.appendChild(node)))
276
+ },
277
+
278
+ async onNavigation (date) {
279
+ this.$emit('navigation', date)
280
+
281
+ /*
282
+ * O componente QDate usa um vue transition de 3ms, como estamos manipulando o DOM, precisamos esperar essa
283
+ * transição terminar para que possamos fazer a logica, com isto precisamos sempre ficar atentos a atualizações
284
+ * do componente QDate para assegurar que esta logica não quebre.
285
+ */
286
+ if (this.useInactiveDates) setTimeout(() => { this.setInactiveEvents(date) }, 350)
287
+
288
+ this.$nextTick(() => this.setNewNavigatorDisplay())
289
+ },
290
+
291
+ toNumberValues (objet) {
292
+ const normalizedObjet = {}
293
+
294
+ for (const key in objet) {
295
+ normalizedObjet[key] = Number(objet[key])
296
+ }
297
+
298
+ return normalizedObjet
299
+ }
300
+ }
301
+ }
302
+ </script>
303
+
304
+ <style lang="scss">
305
+ .qas-date {
306
+ // $
307
+ min-width: 100%;
308
+ width: 100%;
309
+
310
+ &__event-inactive {
311
+ background-color: $grey-4;
312
+ bottom: 4px !important;
313
+ }
314
+
315
+ &--inative {
316
+ .q-date {
317
+ &__calendar-item--fill {
318
+ @include set-typography($subtitle2);
319
+
320
+ color: $grey-4;
321
+ visibility: unset;
322
+ }
323
+ }
324
+ }
325
+
326
+ .q-date__navigation > div:last-child,
327
+ .q-date__navigation > div:first-child {
328
+ min-width: auto;
329
+ width: auto;
330
+ }
331
+
332
+ .q-date {
333
+ &__navigation {
334
+ justify-content: space-between;
335
+
336
+ .q-btn {
337
+ @include set-button(tertiary, false, false, grey-9);
338
+ @include set-typography($subtitle2);
339
+
340
+ .q-icon {
341
+ @include set-typography($subtitle1, true);
342
+ }
343
+ }
344
+ }
345
+
346
+ &__calendar-weekdays {
347
+ color: $grey-8;
348
+
349
+ > div {
350
+ @include set-typography($caption);
351
+
352
+ opacity: 1;
353
+ }
354
+ }
355
+
356
+ &__calendar-item--out {
357
+ color: $grey-4;
358
+ opacity: 1;
359
+ }
360
+
361
+ &__event {
362
+ bottom: -1;
363
+ height: 6px;
364
+ width: 6px;
365
+ }
366
+
367
+ &__months,
368
+ &__years {
369
+ .q-btn {
370
+ background-color: white !important;
371
+
372
+ &.bg-primary {
373
+ color: $primary !important;
374
+ }
375
+
376
+ @include set-button(tertiary, false, false, grey-9);
377
+ }
378
+ }
379
+
380
+ &__view {
381
+ min-height: 230px;
382
+ }
383
+
384
+ &__calendar-days-container {
385
+ color: $grey-9;
386
+ min-height: auto;
387
+
388
+ .q-btn {
389
+ border: 0;
390
+ box-shadow: none;
391
+ color: $grey-9;
392
+ height: 30px !important;
393
+ min-height: 30px;
394
+ min-width: 30px;
395
+ transition: color var(--qas-generic-transition);
396
+ width: 30px !important;
397
+
398
+ .q-ripple,
399
+ .q-focus-helper {
400
+ display: none !important;
401
+ }
402
+
403
+ &:hover {
404
+ color: var(--q-primary-contrast);
405
+ }
406
+
407
+ @include set-typography($subtitle2);
408
+ }
409
+ }
410
+ }
411
+ }
412
+ </style>
@@ -0,0 +1,69 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Componente wrapper do QDate, com estilização própria e funcionalidades extras.
5
+
6
+ props:
7
+ events:
8
+ desc: Marcador de eventos do calendário (repassada para o quasar).
9
+ default: []
10
+ type: [Array, Function]
11
+
12
+ mask:
13
+ desc: Máscara do model (repassada para o quasar).
14
+ default: YYYY-MM-DD
15
+ type: String
16
+ examples: [YYYY-MM-DD, YYYY/MM/DD, DD/MM/YYYY, DD/MM/YYYY HH:mm]
17
+
18
+ model-value:
19
+ desc: Model do componente, usado para v-model (repassada para o quasar).
20
+ default: ''
21
+ type: [String, Array]
22
+ model: true
23
+
24
+ multiple:
25
+ desc: Habilita a seleção múltipla de datas, tornando o model em array (repassada para o quasar).
26
+ default: false
27
+ type: Boolean
28
+
29
+ options:
30
+ desc: Controla quais datas estão habilitadas (repassada para o quasar).
31
+ default: []
32
+ type: [Array, Function]
33
+
34
+ use-iso:
35
+ desc: Transforma o valor das datas do model no padrão ISO 8601.
36
+ type: Boolean
37
+
38
+ use-inactive-dates:
39
+ desc: Habilita o componente a renderizar datas passadas e futuras com seus respectivos eventos.
40
+ default: true
41
+ type: Boolean
42
+
43
+ use-unmask-events:
44
+ desc: A propriedade 'events' do Quasar só funciona com a formatação '/' mesmo que seja passada uma outra máscara. Esta propriedade transforma a formatação da propriedade 'events' de '-' em '/' (mas só funciona caso 'events' seja um Array).
45
+ default: true
46
+ type: Boolean
47
+
48
+ use-unmask-options:
49
+ desc: A propriedade 'options' do Quasar só funciona com a formatação '/' mesmo que seja passada uma outra máscara. Esta propriedade transforma a formatação da propriedade 'options' de '-' em '/' (mas só funciona caso 'options' seja um Array).
50
+ default: true
51
+ type: Boolean
52
+
53
+ events:
54
+ '@update:model-value -> function (value)':
55
+ desc: Dispara toda vez que o model é atualizado, também utilizado para v-model.
56
+ params:
57
+ value:
58
+ desc: Novo valor do v-model
59
+ type: [String, Array]
60
+
61
+ '@navigation -> function ({ month, year })':
62
+ desc: Dispara toda vez que muda de mês/ano.
63
+ params:
64
+ month:
65
+ desc: Mês atual
66
+ type: Number
67
+ year:
68
+ desc: Ano atual
69
+ type: Number
@@ -3,7 +3,7 @@
3
3
  <template #append>
4
4
  <qas-btn v-if="!useTimeOnly" color="grey-9" :disable="$attrs.readonly" icon="sym_r_event" variant="tertiary">
5
5
  <q-popup-proxy ref="dateProxy" transition-hide="scale" transition-show="scale">
6
- <q-date v-model="currentValue" v-bind="defaultDateProps" :mask="maskDate" @update:model-value="updateModelValue" />
6
+ <qas-date v-model="currentValue" v-bind="defaultDateProps" :mask="maskDate" width="290px" @update:model-value="updateModelValue" />
7
7
  </q-popup-proxy>
8
8
  </qas-btn>
9
9
 
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <div class="text-body1 text-grey-8">
3
+ <slot>
4
+ {{ text }}
5
+ </slot>
6
+ </div>
7
+ </template>
8
+
9
+ <script>
10
+ export default {
11
+ name: 'QasEmptyResultText',
12
+
13
+ props: {
14
+ text: {
15
+ default: 'Não há itens para serem exibidos.',
16
+ type: String
17
+ }
18
+ }
19
+ }
20
+ </script>
@@ -0,0 +1,14 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Componente de texto em casos de resultados vazio após filtros.
5
+
6
+ props:
7
+ text:
8
+ desc: Texto a ser exibido.
9
+ default: Não há itens para serem exibidos.
10
+ type: String
11
+
12
+ slots:
13
+ default:
14
+ desc: Slot que substitui a propriedade "text".
@@ -1,27 +1,16 @@
1
1
  <template>
2
- <section class="qas-filters" :class="filtersClass">
2
+ <section :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
- <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" @update:model-value="onSearch">
9
- <template #prepend>
10
- <q-icon v-if="useSearchOnType" color="grey-8" name="sym_r_search" />
11
- <qas-btn v-else color="grey-9" icon="sym_r_search" variant="tertiary" @click="filter()" />
12
- </template>
13
-
14
- <template #append>
15
- <qas-btn v-if="hasSearch" class="q-mr-sm" color="grey-9" icon="sym_r_clear" variant="tertiary" @click="clearSearch" />
16
-
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" ref="filtersButton" v-model="filters" v-bind="filterButtonProps" />
20
- </slot>
21
- </template>
22
- </template>
23
- </qas-input>
24
- </div>
7
+ <qas-search-input v-model="search" :placeholder="searchPlaceholder" :use-search-on-type="useSearchOnType" @filter="filter()" @update:model-value="onSearch">
8
+ <template v-if="showFilterButton" #after-clear>
9
+ <slot :context="mx_context" :filter="filter" :filters="activeFilters" name="filter-button" :remove-filter="removeFilter">
10
+ <pv-filters-button v-if="useFilterButton" ref="filtersButton" v-model="filters" v-bind="filterButtonProps" />
11
+ </slot>
12
+ </template>
13
+ </qas-search-input>
25
14
  </q-form>
26
15
  </slot>
27
16
  </div>
@@ -39,7 +28,9 @@
39
28
 
40
29
  <div v-if="hasChip" class="q-mt-md">
41
30
  <!-- TODO rever com novo estilo -->
42
- <q-chip v-for="(filterItem, key) in activeFilters" :key="key" color="white" :data-cy="`filters-${filterItem.value}-chip`" dense icon-remove="sym_r_close" removable size="md" text-color="grey-8" @remove="removeFilter(filterItem)">{{ getChipValue(filterItem.value) }}</q-chip>
31
+ <q-chip v-for="(filterItem, key) in activeFilters" :key="key" color="white" :data-cy="`filters-${filterItem.value}-chip`" dense icon-remove="sym_r_close" removable size="md" text-color="grey-8" @remove="removeFilter(filterItem)">
32
+ {{ getChipValue(filterItem.value) }}
33
+ </q-chip>
43
34
  </div>
44
35
 
45
36
  <slot :context="mx_context" :filter="filter" :filters="activeFilters" :remove-filter="removeFilter" />
@@ -47,7 +38,6 @@
47
38
  </template>
48
39
 
49
40
  <script>
50
- import QasBtn from '../btn/QasBtn.vue'
51
41
  import PvFiltersButton from './private/PvFiltersButton.vue'
52
42
 
53
43
  import { camelize, camelizeKeys } from 'humps'
@@ -59,7 +49,6 @@ export default {
59
49
  name: 'QasFilters',
60
50
 
61
51
  components: {
62
- QasBtn,
63
52
  PvFiltersButton
64
53
  },
65
54
 
@@ -158,10 +147,6 @@ export default {
158
147
  return activeFilters
159
148
  },
160
149
 
161
- debounce () {
162
- return this.useSearchOnType ? '1200' : ''
163
- },
164
-
165
150
  fields () {
166
151
  return getState.call(this, { entity: this.entity, key: 'filters' })
167
152
  },
@@ -197,10 +182,6 @@ export default {
197
182
  return !!Object.keys(this.fields || {}).length
198
183
  },
199
184
 
200
- hasSearch () {
201
- return this.search.length
202
- },
203
-
204
185
  showFilterButton () {
205
186
  return !!this.$slots.filterButton || this.useFilterButton
206
187
  },
@@ -388,7 +369,7 @@ export default {
388
369
  },
389
370
 
390
371
  onSearch () {
391
- if (this.debounce) {
372
+ if (this.useSearchOnType) {
392
373
  this.filter()
393
374
  }
394
375
  },
@@ -400,48 +381,3 @@ export default {
400
381
  }
401
382
  }
402
383
  </script>
403
-
404
- <style lang="scss">
405
- // TODO rever
406
- .qas-filters {
407
- &__input-content {
408
- .q-field {
409
- &::before {
410
- border: 2px solid transparent;
411
- border-radius: var(--qas-generic-border-radius);
412
- bottom: 0;
413
- content: '';
414
- left: 0;
415
- pointer-events: none;
416
- position: absolute;
417
- right: 0;
418
- top: 0;
419
- transition: border-color var(--qas-generic-transition);
420
- }
421
-
422
- &--dense .q-field__prepend {
423
- padding-right: var(--qas-spacing-xs);
424
- }
425
-
426
- &--dense .q-field__append {
427
- padding-left: var(--qas-spacing-sm);
428
- }
429
-
430
- &--focused::before {
431
- border-color: var(--q-primary);
432
- color: var(--q-primary);
433
- }
434
-
435
- &__control::after,
436
- &__control::before {
437
- display: none !important;
438
- }
439
-
440
- &__native {
441
- padding-bottom: var(--qas-spacing-sm);
442
- padding-top: var(--qas-spacing-sm);
443
- }
444
- }
445
- }
446
- }
447
- </style>
@@ -9,11 +9,11 @@
9
9
 
10
10
  <slot v-if="useActions" name="actions">
11
11
  <qas-actions>
12
- <template #primary>
12
+ <template v-if="useSubmitButton" #primary>
13
13
  <qas-btn class="qas-form-view__btn" :data-cy="`btnSave-${entity}`" :disable="disable" :label="submitButtonLabel" :loading="isSubmitting" type="submit" variant="primary" />
14
14
  </template>
15
15
 
16
- <template #secondary>
16
+ <template v-if="hasCancelButton" #secondary>
17
17
  <qas-btn v-close-popup class="qas-form-view__btn" :data-cy="`btnCancel-${entity}`" :disable="isSubmitting" :label="cancelButtonLabel" type="button" variant="secondary" @click="cancel" />
18
18
  </template>
19
19
  </qas-actions>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="text-subtitle2" :class="classes">
2
+ <div class="text-subtitle1" :class="classes">
3
3
  <slot :label-with-suffix="labelWithSuffix">{{ labelWithSuffix }}</slot>
4
4
  </div>
5
5
  </template>