@bildvitta/quasar-ui-asteroid 3.5.0-beta.8 → 3.5.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.
Files changed (63) hide show
  1. package/package.json +1 -1
  2. package/src/components/actions/QasActions.vue +32 -6
  3. package/src/components/actions/QasActions.yml +11 -1
  4. package/src/components/actions-menu/QasActionsMenu.vue +28 -11
  5. package/src/components/actions-menu/QasActionsMenu.yml +13 -4
  6. package/src/components/alert/QasAlert.vue +1 -1
  7. package/src/components/app-bar/QasAppBar.vue +7 -3
  8. package/src/components/app-menu/QasAppMenu.vue +54 -9
  9. package/src/components/app-user/QasAppUser.vue +8 -4
  10. package/src/components/avatar/QasAvatar.vue +11 -2
  11. package/src/components/avatar/QasAvatar.yml +1 -1
  12. package/src/components/card/QasCard.vue +2 -2
  13. package/src/components/copy/QasCopy.vue +1 -1
  14. package/src/components/copy/QasCopy.yml +1 -1
  15. package/src/components/date-time-input/QasDateTimeInput.vue +67 -6
  16. package/src/components/delete/QasDelete.vue +0 -1
  17. package/src/components/dialog/QasDialog.vue +56 -28
  18. package/src/components/dialog/QasDialog.yml +0 -9
  19. package/src/components/filters/QasFilters.vue +7 -7
  20. package/src/components/form-view/QasFormView.vue +0 -1
  21. package/src/components/gallery/QasGallery.vue +3 -3
  22. package/src/components/gallery/private/PvGalleryCarouselDialog.vue +3 -3
  23. package/src/components/header-actions/QasHeaderActions.vue +62 -0
  24. package/src/components/header-actions/QasHeaderActions.yml +26 -0
  25. package/src/components/list-items/QasListItems.vue +1 -1
  26. package/src/components/list-items/QasListItems.yml +1 -1
  27. package/src/components/nested-fields/QasNestedFields.vue +4 -4
  28. package/src/components/nested-fields/QasNestedFields.yml +2 -2
  29. package/src/components/numeric-input/QasNumericInput.vue +9 -0
  30. package/src/components/page-header/QasPageHeader.vue +92 -17
  31. package/src/components/page-header/QasPageHeader.yml +19 -1
  32. package/src/components/pagination/QasPagination.vue +12 -1
  33. package/src/components/password-input/QasPasswordInput.vue +1 -1
  34. package/src/components/search-box/QasSearchBox.vue +3 -3
  35. package/src/components/select/QasSelect.vue +6 -5
  36. package/src/components/select-list/QasSelectList.vue +1 -1
  37. package/src/components/signature-pad/QasSignaturePad.vue +1 -1
  38. package/src/components/signature-uploader/QasSignatureUploader.vue +7 -8
  39. package/src/components/single-view/QasSingleView.vue +1 -1
  40. package/src/components/table-generator/QasTableGenerator.vue +29 -4
  41. package/src/components/tabs-generator/QasTabsGenerator.vue +140 -37
  42. package/src/components/tabs-generator/QasTabsGenerator.yml +4 -24
  43. package/src/components/tabs-generator/private/PvTabsGeneratorStatus.vue +32 -0
  44. package/src/components/text-truncate/QasTextTruncate.vue +0 -1
  45. package/src/components/transfer/QasTransfer.vue +2 -2
  46. package/src/components/tree-generator/QasTreeGenerator.vue +4 -5
  47. package/src/components/uploader/QasUploader.vue +8 -8
  48. package/src/components/welcome/QasWelcome.vue +108 -0
  49. package/src/components/welcome/QasWelcome.yml +14 -0
  50. package/src/components/welcome/private/PvWelcomeShortcutCard.vue +58 -0
  51. package/src/css/components/base.scss +8 -0
  52. package/src/css/components/item.scss +8 -3
  53. package/src/css/mixins/index.scss +1 -0
  54. package/src/css/mixins/set-typography.scss +8 -0
  55. package/src/css/variables/index.scss +1 -0
  56. package/src/css/variables/shadow.scss +3 -0
  57. package/src/css/variables/spacing.scss +15 -0
  58. package/src/css/variables/typography.scss +1 -1
  59. package/src/mixins/delete.js +0 -1
  60. package/src/plugins/notify-error/NotifyError.js +1 -1
  61. package/src/plugins/notify-success/NotifySuccess.js +1 -1
  62. package/src/shared/date-config.js +26 -0
  63. package/src/vue-plugin.js +6 -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.5.0-beta.8",
4
+ "version": "3.5.0",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "dist/asteroid.cjs.min.js",
@@ -1,10 +1,10 @@
1
1
  <template>
2
- <div :class="classes">
3
- <div class="col-12 col-sm-auto">
2
+ <div class="qas-actions" :class="classes">
3
+ <div v-if="hasSecondarySlot" :class="columnClasses">
4
4
  <slot name="secondary" />
5
5
  </div>
6
6
 
7
- <div class="col-12 col-sm-auto">
7
+ <div v-if="hasPrimarySlot" :class="columnClasses">
8
8
  <slot name="primary" />
9
9
  </div>
10
10
  </div>
@@ -22,9 +22,17 @@ export default {
22
22
  },
23
23
 
24
24
  gutter: {
25
- default: 'md',
25
+ default: '',
26
26
  type: String,
27
- validator: value => ['xs', 'sm', 'md', 'lg', 'xl'].includes(value)
27
+ validator: value => !value || ['xs', 'sm', 'md', 'lg', 'xl'].includes(value)
28
+ },
29
+
30
+ useFullWidth: {
31
+ type: Boolean
32
+ },
33
+
34
+ useEqualWidth: {
35
+ type: Boolean
28
36
  }
29
37
  },
30
38
 
@@ -32,9 +40,27 @@ export default {
32
40
  classes () {
33
41
  return [
34
42
  `justify-${this.align}`,
35
- `q-col-gutter-${this.gutter}`,
43
+ `q-col-gutter-${this.defaultGutter}`,
36
44
  this.$qas.screen.isSmall ? 'column reverse' : 'row'
37
45
  ]
46
+ },
47
+
48
+ defaultGutter () {
49
+ return this.gutter || this.$qas.screen.isSmall ? 'md' : 'lg'
50
+ },
51
+
52
+ columnClasses () {
53
+ if (this.useEqualWidth) return 'col-12 col-sm-6'
54
+
55
+ return this.useFullWidth ? 'col-12' : 'col-12 col-sm-auto'
56
+ },
57
+
58
+ hasPrimarySlot () {
59
+ return !!this.$slots.primary
60
+ },
61
+
62
+ hasSecondarySlot () {
63
+ return !!this.$slots.secondary
38
64
  }
39
65
  }
40
66
  }
@@ -12,10 +12,20 @@ props:
12
12
 
13
13
  gutter:
14
14
  desc: Espaçamento entre os elementos.
15
- default: md
15
+ default: lg
16
16
  type: String
17
17
  examples: ['xs', 'sm', 'md', 'lg', 'xl']
18
18
 
19
+ use-full-width:
20
+ desc: Deixa as colunas 100%, com col-12.
21
+ default: false
22
+ type: Boolean
23
+
24
+ use-equal-width:
25
+ desc: Deixa as colunas 50% no desktop e 100% no mobile, col-12 col-sm-6.
26
+ default: false
27
+ type: Boolean
28
+
19
29
  slots:
20
30
  primary:
21
31
  desc: 'Slot para ação primaria (ex: botão de salvar), em telas menores que sm, se torna o primeiro elemento.'
@@ -16,6 +16,10 @@
16
16
  </slot>
17
17
  </q-list>
18
18
  </q-menu>
19
+
20
+ <q-tooltip v-if="hasTooltip" class="text-caption">
21
+ {{ tooltipLabel }}
22
+ </q-tooltip>
19
23
  </component>
20
24
  </div>
21
25
  </template>
@@ -33,13 +37,13 @@ export default {
33
37
  },
34
38
 
35
39
  props: {
36
- icon: {
37
- default: 'o_more_vert',
40
+ color: {
41
+ default: '',
38
42
  type: String
39
43
  },
40
44
 
41
- label: {
42
- default: 'Opções',
45
+ icon: {
46
+ default: 'sym_r_more_vert',
43
47
  type: String
44
48
  },
45
49
 
@@ -55,7 +59,7 @@ export default {
55
59
 
56
60
  deleteIcon: {
57
61
  type: String,
58
- default: 'o_delete'
62
+ default: 'sym_r_delete'
59
63
  },
60
64
 
61
65
  deleteProps: {
@@ -63,9 +67,13 @@ export default {
63
67
  type: Object
64
68
  },
65
69
 
66
- useLabelOnSmallScreen: {
70
+ useLabel: {
67
71
  default: true,
68
72
  type: Boolean
73
+ },
74
+
75
+ useLabelOnSmallScreen: {
76
+ type: Boolean
69
77
  }
70
78
  },
71
79
 
@@ -89,14 +97,15 @@ export default {
89
97
  component () {
90
98
  const props = {}
91
99
 
100
+ // TODO: solução do color é temporária até ser definido o novo QasBtn.
92
101
  if (this.hasMoreThanOneAction) {
93
- props.label = 'Opções'
102
+ props.color = this.color || 'grey-9'
94
103
  props.iconRight = this.icon
95
- props.textColor = 'dark'
104
+ props.label = this.useLabel ? 'Opções' : ''
96
105
  } else {
106
+ props.color = this.color || 'primary'
97
107
  props.icon = this.actions[this.firstItemKey]?.icon
98
- props.label = this.actions[this.firstItemKey]?.label
99
- props.color = 'primary'
108
+ props.label = this.useLabel ? this.tooltipLabel : ''
100
109
  }
101
110
 
102
111
  this.hasDelete && Object.assign(props, this.deleteProps)
@@ -120,7 +129,15 @@ export default {
120
129
  },
121
130
 
122
131
  hasMoreThanOneAction () {
123
- return Object.keys(this.actions || {}).length > 1
132
+ return Object.keys(this.list || {}).length + Number(this.hasDelete) > 1
133
+ },
134
+
135
+ hasTooltip () {
136
+ return !this.hasMoreThanOneAction && !this.useLabel
137
+ },
138
+
139
+ tooltipLabel () {
140
+ return this.actions[this.firstItemKey]?.label
124
141
  }
125
142
  },
126
143
 
@@ -4,9 +4,13 @@ meta:
4
4
  desc: Componente para abrir um menu de ação a partir de um botão, muito utilizado em tela de edição.
5
5
 
6
6
  props:
7
+ color:
8
+ desc: Cor do ícone.
9
+ type: String
10
+
7
11
  delete-icon:
8
12
  desc: Ícone do botão de deletar.
9
- default: o_delete
13
+ default: sym_r_delete
10
14
  type: String
11
15
 
12
16
  delete-label:
@@ -21,7 +25,7 @@ props:
21
25
 
22
26
  icon:
23
27
  desc: Ícone do botão.
24
- default: o_more_vert
28
+ default: sym_r_more_vert
25
29
  type: String
26
30
  examples: [start, end, between, around, center]
27
31
 
@@ -32,7 +36,7 @@ props:
32
36
  examples: [
33
37
  "{
34
38
  delete: {
35
- icon: 'o_visibility',
39
+ icon: 'sym_r_visibility',
36
40
  label: 'Visualizar',
37
41
  handler: () => alert('handler ativado')
38
42
  }
@@ -44,6 +48,11 @@ props:
44
48
  default: true
45
49
  type: Boolean
46
50
 
51
+ use-label:
52
+ desc: Habilita ou não o label no botão.
53
+ default: true
54
+ type: Boolean
55
+
47
56
  slots:
48
57
  '[nome-da-chave]':
49
58
  desc: 'Slot dinâmico gerado a partir das chaves dentro do objeto da prop "list"'
@@ -56,7 +65,7 @@ slots:
56
65
  examples: [
57
66
  "
58
67
  {
59
- icon: 'o_delete',
68
+ icon: 'sym_r_delete',
60
69
  label: 'Delete',
61
70
  }
62
71
  "
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div v-if="model" class="bg-white q-pa-lg qas-alert relative-position rounded-borders" :class="classes">
3
- <qas-btn class="absolute-top-right q-mr-md q-mt-sm" :color="color" dense flat icon="o_close" rounded @click="close" />
3
+ <qas-btn class="absolute-top-right q-mr-md q-mt-sm" :color="color" dense flat icon="sym_r_close" rounded @click="close" />
4
4
 
5
5
  <div class="q-gutter-md q-mr-lg">
6
6
  <slot name="header">
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <q-header class="qas-app-bar shadow-2" height-hint="56">
3
3
  <q-toolbar class="bg-white qas-app-bar__toolbar text-grey-9">
4
- <qas-btn color="grey-7" dense flat icon="o_menu" round @click="toggleMenuDrawer" />
4
+ <qas-btn color="grey-7" dense flat icon="sym_r_menu" round @click="toggleMenuDrawer" />
5
5
 
6
6
  <q-toolbar-title>
7
- <router-link class="flex items-center no-wrap text-no-decoration" :to="rootRoute">
7
+ <router-link class="flex items-center no-wrap text-no-decoration" :class="routerLinkClass" :to="rootRoute">
8
8
  <img v-if="brand" :alt="title" class="qas-app-bar__brand" :src="brand">
9
9
  <span v-else class="ellipsis text-bold text-primary">{{ title }}</span>
10
10
  <q-badge v-if="hasDevelopmentBadge" class="q-ml-sm" color="red" :label="developmentBadgeLabel" />
@@ -97,6 +97,10 @@ export default {
97
97
 
98
98
  rootRoute () {
99
99
  return this.$router.hasRoute('Root') ? { name: 'Root' } : { path: '/' }
100
+ },
101
+
102
+ routerLinkClass () {
103
+ return this.$qas.screen.isSmall && 'justify-center'
100
104
  }
101
105
  },
102
106
 
@@ -119,7 +123,7 @@ export default {
119
123
  }
120
124
 
121
125
  &__brand {
122
- height: 24px;
126
+ max-width: 164px;
123
127
  }
124
128
  }
125
129
  </style>
@@ -1,30 +1,36 @@
1
1
  <template>
2
- <q-drawer v-model="model" :behavior="behavior" class="qas-app-menu" :width="280">
3
- <div class="column full-height justify-between">
2
+ <q-drawer v-model="model" :behavior="behavior" :width="drawerWidth">
3
+ <div class="column full-height justify-between qas-app-menu">
4
4
  <div class="full-width">
5
5
  <!-- Brand -->
6
6
  <div v-if="!$qas.screen.untilLarge" class="q-pt-xl q-px-lg">
7
7
  <router-link class="block q-toolbar__title relative-position text-no-decoration" :to="rootRoute">
8
- <img v-if="brand" :alt="title" class="full-width" :src="brand">
8
+ <img v-if="brand" :alt="title" class="qas-app-menu__brand" :src="brand">
9
9
  <span v-else class="ellipsis text-bold text-primary">{{ title }}</span>
10
10
  <q-badge v-if="hasDevelopmentBadge" color="red" floating :label="developmentBadgeLabel" />
11
11
  </router-link>
12
12
  </div>
13
13
 
14
14
  <!-- Module -->
15
- <div v-if="displayModuleSection" class="q-mt-xl q-px-lg qas-app-menu__module">
16
- <qas-select v-model="module" borderless class="q-py-xs qas-app-menu__select shadow-2" dense input-class="q-px-md" :options="defaultModules" :outlined="false" :use-search="false" @update:model-value="redirectHandler(currentModelOption)" />
15
+ <div v-if="displayModuleSection" class="items-center justify-between no-wrap q-mt-xl q-px-lg qas-app-menu__module row">
16
+ <div class="full-width">
17
+ <qas-select v-model="module" borderless class="q-py-xs qas-app-menu__select shadow-2" dense input-class="q-px-md" :options="defaultModules" :outlined="false" :use-search="false" @update:model-value="redirectHandler(currentModelOption)" />
18
+ </div>
19
+
20
+ <div v-if="$qas.screen.isSmall" class="q-ml-xl">
21
+ <qas-btn color="grey-9" dense flat icon="sym_r_close" rounded @click="closeDrawer" />
22
+ </div>
17
23
  </div>
18
24
 
19
25
  <!-- Menu -->
20
26
  <q-list v-if="items.length" class="q-mt-xl qas-app-menu__menu text-grey-9">
21
27
  <template v-for="(menuItem, index) in items">
22
- <div v-if="hasChildren(menuItem)" :key="`children-${index}`">
23
- <q-item class="items-center q-pb-none q-pt-md text-weight-bold">
28
+ <div v-if="hasChildren(menuItem)" :key="`children-${index}`" class="qas-app-menu__content">
29
+ <q-item class="items-center q-pb-none q-pt-md qas-app-menu__item qas-app-menu__item--label text-weight-bold">
24
30
  {{ menuItem.label }}
25
31
  </q-item>
26
32
 
27
- <q-item v-for="(menuChildItem, childIndex) in menuItem.children" :key="childIndex" :active="isActive(menuChildItem)" :to="getRouterRedirect(menuChildItem)">
33
+ <q-item v-for="(menuChildItem, childIndex) in menuItem.children" :key="childIndex" :active="isActive(menuChildItem)" class="qas-app-menu__children qas-app-menu__item qas-app-menu__item--children" :to="getRouterRedirect(menuChildItem)">
28
34
  <q-item-section v-if="menuChildItem.icon" avatar>
29
35
  <q-icon :name="menuChildItem.icon" />
30
36
  </q-item-section>
@@ -35,7 +41,7 @@
35
41
  </q-item>
36
42
  </div>
37
43
 
38
- <q-item v-else :key="index" :active="isActive(menuItem)" active-class="q-router-link--active" :to="getRouterRedirect(menuItem)">
44
+ <q-item v-else :key="index" :active="isActive(menuItem)" active-class="q-router-link--active" class="qas-app-menu__item" :to="getRouterRedirect(menuItem)">
39
45
  <q-item-section v-if="menuItem.icon" avatar>
40
46
  <q-icon :name="menuItem.icon" />
41
47
  </q-item-section>
@@ -165,6 +171,10 @@ export default {
165
171
  return this.defaultModules.length
166
172
  },
167
173
 
174
+ drawerWidth () {
175
+ return this.$qas.screen.isSmall ? 320 : 280
176
+ },
177
+
168
178
  hasDevelopmentBadge () {
169
179
  return !!this.developmentBadgeLabel
170
180
  },
@@ -237,6 +247,10 @@ export default {
237
247
 
238
248
  signOut () {
239
249
  this.$emit('sign-out')
250
+ },
251
+
252
+ closeDrawer () {
253
+ this.$emit('update:modelValue', false)
240
254
  }
241
255
  }
242
256
  }
@@ -255,6 +269,11 @@ export default {
255
269
  }
256
270
  }
257
271
 
272
+ &__brand {
273
+ max-width: 208px;
274
+ width: 100%;
275
+ }
276
+
258
277
  &__menu .q-item {
259
278
  padding-left: var(--qas-spacing-lg);
260
279
  }
@@ -263,6 +282,32 @@ export default {
263
282
  border-radius: 4px;
264
283
  }
265
284
 
285
+ &__item {
286
+ &:not(&--label) + &:not(&--label) {
287
+ margin-top: var(--qas-spacing-sm);
288
+ }
289
+
290
+ &--label {
291
+ margin-bottom: var(--qas-spacing-md);
292
+ min-height: 0;
293
+ padding-top: 0;
294
+ }
295
+
296
+ &--children.q-item {
297
+ padding-left: calc(var(--qas-spacing-xl) + var(--qas-spacing-sm));
298
+
299
+ & + & {
300
+ margin-top: var(--qas-spacing-sm);
301
+ }
302
+ }
303
+ }
304
+
305
+ &__content + &__content,
306
+ &__item + &__content,
307
+ &__content + &__item {
308
+ margin-top: var(--qas-spacing-lg);
309
+ }
310
+
266
311
  // User
267
312
  .qas-app-user__data {
268
313
  line-height: 1.25;
@@ -17,10 +17,10 @@
17
17
  <div class="ellipsis qas-app-user__menu-name">{{ userName }}</div>
18
18
  <div class="ellipsis">{{ user.email }}</div>
19
19
 
20
- <q-list class="q-mt-sm">
20
+ <q-list class="q-mt-md">
21
21
  <q-item v-close-popup :active="false" class="qas-app-user__menu-item" clickable :to="user.to">
22
22
  <q-item-section avatar>
23
- <q-icon name="o_person" size="20px" />
23
+ <q-icon name="sym_r_person" />
24
24
  </q-item-section>
25
25
 
26
26
  <q-item-section>Editar</q-item-section>
@@ -28,7 +28,7 @@
28
28
 
29
29
  <q-item v-if="hasNotifications" v-close-popup class="qas-app-user__menu-item" clickable>
30
30
  <q-item-section avatar>
31
- <q-icon name="o_notifications" size="20px" />
31
+ <q-icon name="sym_r_notifications" />
32
32
  </q-item-section>
33
33
 
34
34
  <q-item-section>Notificações</q-item-section>
@@ -40,7 +40,7 @@
40
40
 
41
41
  <q-item v-close-popup class="qas-app-user__menu-item" clickable @click="signOut">
42
42
  <q-item-section avatar>
43
- <q-icon name="o_logout" size="20px" />
43
+ <q-icon name="sym_r_logout" />
44
44
  </q-item-section>
45
45
 
46
46
  <q-item-section>Sair</q-item-section>
@@ -130,6 +130,10 @@ export default {
130
130
  &__menu-item {
131
131
  min-height: 36px;
132
132
  padding: 0;
133
+
134
+ & + & {
135
+ margin-top: var(--qas-spacing-sm);
136
+ }
133
137
  }
134
138
 
135
139
  @media (max-width: $breakpoint-xs) {
@@ -25,7 +25,7 @@ export default {
25
25
  },
26
26
 
27
27
  icon: {
28
- default: 'o_error',
28
+ default: 'sym_r_error',
29
29
  type: String
30
30
  },
31
31
 
@@ -60,6 +60,14 @@ export default {
60
60
  },
61
61
 
62
62
  attributes () {
63
+ const {
64
+ rounded,
65
+ square,
66
+ fontSize,
67
+ textColor,
68
+ ...attributes
69
+ } = this.$attrs
70
+
63
71
  const colors = {
64
72
  primary: 'white',
65
73
  'secondary-contrast': 'primary'
@@ -68,7 +76,8 @@ export default {
68
76
  return {
69
77
  size: this.size,
70
78
  color: this.color,
71
- textColor: colors[this.color]
79
+ textColor: colors[this.color],
80
+ ...attributes
72
81
  }
73
82
  }
74
83
  },
@@ -18,7 +18,7 @@ props:
18
18
 
19
19
  icon:
20
20
  desc: Ícone de fundo do avatar que vai aparecer caso não tenha imagem nem título.
21
- default: o_error
21
+ default: sym_r_error
22
22
  type: String
23
23
 
24
24
  image:
@@ -3,7 +3,7 @@
3
3
  <q-card class="border-radius-lg column full-height overflow-hidden" :class="cardClasses">
4
4
  <header v-if="useHeader" class="overflow-hidden relative-position w-full">
5
5
  <slot name="header">
6
- <q-carousel v-model="slideImage" animated class="cursor-pointer" height="205px" infinite :navigation="hasImages" navigation-icon="o_fiber_manual_record" swipeable>
6
+ <q-carousel v-model="slideImage" animated class="cursor-pointer" height="205px" infinite :navigation="hasImages" navigation-icon="sym_r_fiber_manual_record" swipeable>
7
7
  <template #navigation-icon="{ active, btnProps, onClick }">
8
8
  <qas-btn color="white" dense flat :icon="getNavigationIcon(active, btnProps)" round size="sm" @click="onClick" />
9
9
  </template>
@@ -106,7 +106,7 @@ export default {
106
106
 
107
107
  methods: {
108
108
  getNavigationIcon (active, { icon }) {
109
- return active ? 'o_radio_button_checked' : icon
109
+ return active ? 'sym_r_radio_button_checked' : icon
110
110
  }
111
111
  }
112
112
  }
@@ -16,7 +16,7 @@ export default {
16
16
 
17
17
  props: {
18
18
  icon: {
19
- default: 'o_file_copy',
19
+ default: 'sym_r_file_copy',
20
20
  type: String
21
21
  },
22
22
 
@@ -7,7 +7,7 @@ props:
7
7
  icon:
8
8
  desc: Ícone para mostrar no botão.
9
9
  type: String
10
- default: o_file_copy
10
+ default: sym_r_file_copy
11
11
 
12
12
  text:
13
13
  desc: Texto a ser copiado.
@@ -1,13 +1,13 @@
1
1
  <template>
2
- <qas-input ref="input" v-bind="attributes" v-model="currentValue" :unmasked-value="false" @update:model-value="updateModelValue">
2
+ <qas-input ref="input" v-bind="attributes" v-model="currentValue" :unmasked-value="false" @blur="validateDateTimeOnBlur" @focus="resetError" @update:model-value="updateModelValue">
3
3
  <template #append>
4
- <q-icon v-if="!useTimeOnly" class="cursor-pointer" name="o_event">
4
+ <q-icon v-if="!useTimeOnly" class="cursor-pointer" name="sym_r_event">
5
5
  <q-popup-proxy ref="dateProxy" transition-hide="scale" transition-show="scale">
6
6
  <q-date v-model="currentValue" v-bind="dateProps" :mask="maskDate" @update:model-value="updateModelValue" />
7
7
  </q-popup-proxy>
8
8
  </q-icon>
9
9
 
10
- <q-icon v-if="!useDateOnly" class="cursor-pointer q-ml-md" name="o_access_time">
10
+ <q-icon v-if="!useDateOnly" class="cursor-pointer q-ml-md" name="sym_r_access_time">
11
11
  <q-popup-proxy ref="timeProxy" transition-hide="scale" transition-show="scale">
12
12
  <q-time v-model="currentValue" v-bind="timeProps" format24h :mask="maskDate" @update:model-value="updateModelValue" />
13
13
  </q-popup-proxy>
@@ -69,6 +69,9 @@ export default {
69
69
  data () {
70
70
  return {
71
71
  currentValue: '',
72
+ error: false,
73
+ errorMessage: '',
74
+ hasInvalidDate: false,
72
75
  lastValue: ''
73
76
  }
74
77
  },
@@ -78,6 +81,8 @@ export default {
78
81
  const { modelValue, ...attributes } = this.$attrs
79
82
 
80
83
  return {
84
+ error: this.error,
85
+ errorMessage: this.errorMessage,
81
86
  ...attributes,
82
87
  mask: this.mask
83
88
  }
@@ -131,6 +136,16 @@ export default {
131
136
  this.currentValue = value
132
137
  const valueLength = value?.replace?.(/_/g, '')?.length
133
138
 
139
+ this.error = this.validateDateAndTime(value)
140
+
141
+ if (this.error) {
142
+ this.hasInvalidDate = true
143
+ this.errorMessage = 'Data inválida.'
144
+ return
145
+ }
146
+
147
+ this.hasInvalidDate = false
148
+
134
149
  if (value === '' || valueLength === this.mask.length) {
135
150
  this.lastValue = this.useTimeOnly ? value : this.toISOString(value)
136
151
  this.$emit('update:modelValue', this.lastValue)
@@ -146,9 +161,7 @@ export default {
146
161
  },
147
162
 
148
163
  toISOString (value) {
149
- if (!value) {
150
- return ''
151
- }
164
+ if (!value) return ''
152
165
 
153
166
  if (this.useDateOnly && !this.useIso) {
154
167
  return dateFn(date.extractDate(value, this.maskDate), 'yyyy-MM-dd')
@@ -172,6 +185,54 @@ export default {
172
185
  this.useDateOnly ? newDate.slice(0, 23) : newDate,
173
186
  this.maskDate
174
187
  )
188
+ },
189
+
190
+ validateDateTimeOnBlur () {
191
+ const valueLength = this.currentValue?.replace?.(/_/g, '')?.length
192
+
193
+ // valida se o tamanho digitado é o tamanho que a mascara espera receber
194
+ this.error = !!((valueLength < this.mask.length || this.error) && valueLength)
195
+
196
+ if (this.error && !this.hasInvalidDate) {
197
+ this.errorMessage = 'Data incompleta.'
198
+ }
199
+
200
+ if (this.hasInvalidDate) {
201
+ this.currentValue = ''
202
+ }
203
+
204
+ if (this.error || this.hasInvalidDate) {
205
+ this.$emit('update:modelValue', '')
206
+ }
207
+ },
208
+
209
+ validateDateAndTime (value) {
210
+ if (!value) return false
211
+
212
+ if (this.useDateOnly) return this.validateDateOnly(value)
213
+ if (this.useTimeOnly) return this.validateTimeOnly(value)
214
+
215
+ const [date, time] = value?.split(' ')
216
+
217
+ return this.validateDateOnly(date) || this.validateTimeOnly(time)
218
+ },
219
+
220
+ validateDateOnly (value = '') {
221
+ const [day, month] = value.split('/')
222
+
223
+ return day > 31 || month > 12
224
+ },
225
+
226
+ validateTimeOnly (value = '') {
227
+ const [hour, minute] = value.split(':')
228
+
229
+ return hour > 23 || minute > 59
230
+ },
231
+
232
+ resetError () {
233
+ if (!this.currentValue) {
234
+ this.error = false
235
+ }
175
236
  }
176
237
  }
177
238
  }
@@ -77,7 +77,6 @@ export default {
77
77
  defaultDialogProps () {
78
78
  return {
79
79
  card: {
80
- title: 'Confirmar',
81
80
  description: 'Tem certeza que deseja excluir este item?'
82
81
  },
83
82