@bildvitta/quasar-ui-asteroid 3.5.0-beta.1 → 3.5.0-beta.3

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.5.0-beta.1",
4
+ "version": "3.5.0-beta.3",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "dist/asteroid.cjs.min.js",
@@ -1,72 +1,32 @@
1
1
  <template>
2
- <q-header class="bg-white qas-app-bar shadow-2" height-hint="70">
3
- <q-toolbar class="q-px-md qas-app-bar__toolbar" color="bg-white">
4
- <q-ajax-bar color="white" position="top" size="2px" />
5
-
6
- <qas-btn v-if="$qas.screen.untilLarge" color="grey-7" dense flat icon="o_menu" round @click="toggleMenuDrawer" />
7
-
8
- <q-toolbar-title class="flex" :class="toolbarTitleClass">
9
- <div class="cursor-pointer" @click="goToRoot">
10
- <img v-if="brand" :alt="title" class="q-mr-sm qas-app-bar__brand" :src="brand">
11
- <span v-if="showTitle" class="text-bold text-grey-9 text-subtitle1 text-uppercase">{{ title }}</span>
12
- <q-badge v-if="hasDevelopmentBadge" align="middle" class="q-ml-sm" color="negative" :label="developmentBadgeLabel" />
13
- </div>
2
+ <q-header class="qas-app-bar shadow-2" height-hint="56">
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" />
5
+
6
+ <q-toolbar-title>
7
+ <router-link class="flex items-center no-wrap text-no-decoration" :to="rootRoute">
8
+ <img v-if="brand" :alt="title" class="qas-app-bar__brand" :src="brand">
9
+ <span v-else class="ellipsis text-bold text-primary">{{ title }}</span>
10
+ <q-badge v-if="hasDevelopmentBadge" class="q-ml-sm" color="red" :label="developmentBadgeLabel" />
11
+ </router-link>
14
12
  </q-toolbar-title>
15
13
 
16
- <div v-if="hasNotifications" class="q-mr-md">
17
- <qas-btn class="q-mr-md" dense icon="o_notifications" round unelevated>
18
- <q-badge v-if="notifications" color="red" floating>{{ notifications.count }}</q-badge>
19
- </qas-btn>
20
- </div>
21
-
22
- <div class="items-center no-wrap q-gutter-md row">
23
- <slot name="tools" />
24
-
25
- <div v-if="hasUser" class="cursor-pointer items-center no-wrap qas-app-bar__user-content rounded-borders row text-grey-9" :title="userName">
26
- <qas-avatar color="white" dark :image="user.photo" rounded size="42px" text-color="primary" :title="userName" />
27
-
28
- <div class="q-ml-md q-pr-sm qas-app-bar__user-data qs-lh-lg">
29
- <div class="ellipsis q-mb-xs qas-app-bar__user-name">{{ userName }}</div>
30
- <div class="ellipsis qas-app-bar__user-email">{{ user.email }}</div>
31
- </div>
32
-
33
- <q-menu anchor="bottom end" class="shadow-2 text-grey-9" max-height="400px" :offset="[0, 5]" self="top end">
34
- <div class="qas-app-bar__user-menu">
35
- <div class="q-pa-lg text-center">
36
- <button class="unset" @click="goToProfile">
37
- <qas-avatar :image="user.photo" size="145px" :title="userName" />
38
- </button>
39
-
40
- <div class="ellipsis q-mt-lg qas-app-bar__user-name qs-lh-sm">{{ userName }}</div>
41
- <div class="ellipsis q-mt-xs">{{ user.email }}</div>
42
-
43
- <div class="q-mt-sm">
44
- <qas-btn flat icon="o_edit" label="Editar" :to="user.to" />
45
- </div>
46
-
47
- <div class="q-mt-sm">
48
- <qas-btn v-close-popup class="q-px-lg q-py-xs" dense icon="o_exit_to_app" label="Sair" outline @click="signOut" />
49
- </div>
50
-
51
- <slot name="user" :user="user" />
52
- </div>
53
- </div>
54
- </q-menu>
55
- </div>
56
- </div>
14
+ <slot v-if="hasUser" name="user" :user="user">
15
+ <qas-app-user :menu-props="appUserMenuProps" :user="user" @sign-out="signOut" />
16
+ </slot>
57
17
  </q-toolbar>
58
18
  </q-header>
59
19
  </template>
60
20
 
61
21
  <script>
62
- import QasAvatar from '../avatar/QasAvatar.vue'
22
+ import QasAppUser from '../app-user/QasAppUser.vue'
63
23
  import QasBtn from '../btn/QasBtn.vue'
64
24
 
65
25
  export default {
66
26
  name: 'QasAppBar',
67
27
 
68
28
  components: {
69
- QasAvatar,
29
+ QasAppUser,
70
30
  QasBtn
71
31
  },
72
32
 
@@ -76,16 +36,16 @@ export default {
76
36
  type: String
77
37
  },
78
38
 
79
- title: {
80
- type: String,
81
- default: ''
82
- },
83
-
84
39
  notifications: {
85
40
  default: () => ({}),
86
41
  type: Object
87
42
  },
88
43
 
44
+ title: {
45
+ required: true,
46
+ type: String
47
+ },
48
+
89
49
  user: {
90
50
  default: () => ({}),
91
51
  require: true,
@@ -102,6 +62,14 @@ export default {
102
62
  },
103
63
 
104
64
  computed: {
65
+ appUserMenuProps () {
66
+ return {
67
+ anchor: 'bottom end',
68
+ offset: [0, 5],
69
+ self: 'top end'
70
+ }
71
+ },
72
+
105
73
  developmentBadgeLabel () {
106
74
  const hosts = {
107
75
  localhost: 'Local',
@@ -123,40 +91,16 @@ export default {
123
91
  return !!this.developmentBadgeLabel
124
92
  },
125
93
 
126
- hasNotifications () {
127
- return !!Object.keys(this.notifications).length
128
- },
129
-
130
- showTitle () {
131
- return this.title && !this.brand
132
- },
133
-
134
94
  hasUser () {
135
95
  return !!Object.keys(this.user).length
136
96
  },
137
97
 
138
- userName () {
139
- return this.user.name || this.user.givenName
140
- },
141
-
142
- toolbarTitleClass () {
143
- return !this.$qas.screen.untilLarge && 'q-pl-none'
98
+ rootRoute () {
99
+ return this.$router.hasRoute('Root') ? { name: 'Root' } : { path: '/' }
144
100
  }
145
101
  },
146
102
 
147
103
  methods: {
148
- goToProfile () {
149
- return this.$router.push(this.user.to)
150
- },
151
-
152
- goToRoot () {
153
- const hasRoot = this.$router.hasRoute('Root')
154
-
155
- this.$router.push({
156
- ...(hasRoot ? { name: 'Root' } : { path: '/' })
157
- })
158
- },
159
-
160
104
  signOut () {
161
105
  this.$emit('sign-out')
162
106
  },
@@ -171,39 +115,11 @@ export default {
171
115
  <style lang="scss">
172
116
  .qas-app-bar {
173
117
  &__toolbar {
174
- height: 70px;
118
+ height: 56px;
175
119
  }
176
120
 
177
121
  &__brand {
178
122
  height: 24px;
179
- position: relative;
180
- top: 4px;
181
- }
182
-
183
- &__user-content {
184
- width: 230px;
185
- }
186
-
187
- &__user-data {
188
- max-width: 180px;
189
- }
190
-
191
- &__user-name {
192
- font-weight: 600;
193
- }
194
-
195
- &__user-menu {
196
- width: 260px;
197
- }
198
-
199
- @media (max-width: $breakpoint-xs) {
200
- &__user-content {
201
- width: auto;
202
- }
203
-
204
- &__user-data {
205
- display: none;
206
- }
207
123
  }
208
124
  }
209
125
  </style>
@@ -1,20 +1,25 @@
1
1
  type: component
2
2
 
3
3
  meta:
4
- desc: Cria um seção para alertar sobre um conteúdo.
4
+ desc: Gerencia a barra superior da aplicação
5
5
 
6
6
  props:
7
+ brand:
8
+ desc: Path do logotipo.
9
+ type: String
10
+
7
11
  notifications:
8
- desc: Ícone de notificação que fica ao lado do menu de usuário.
12
+ desc: Lista com as notificações do usuário.
9
13
  type: Object
10
14
  default: {}
11
15
 
12
16
  title:
13
- desc: Título do header, normalmente usado como título da aplicação.
17
+ desc: Título da aplicação.
18
+ required: true
14
19
  type: String
15
20
 
16
21
  user:
17
- desc: Informações de usuário para ser usado no menu.
22
+ desc: Informações do usuário.
18
23
  type: Object
19
24
  default: {}
20
25
  examples: [
@@ -29,16 +34,13 @@ props:
29
34
 
30
35
  slots:
31
36
  user:
32
- desc: Slot para acessar o menu de usuário
37
+ desc: Slot para acessar o menu de usuário.
33
38
  scope:
34
39
  user:
35
- desc: Informações de usuário para ser usado no menu, valor vai depender da prop "user".
40
+ desc: Informações do usuário, recebe o objeto "user".
36
41
  type: Object
37
42
  default: {}
38
43
 
39
- tools:
40
- desc: Slot para acessar o header após o elemento de notificações.
41
-
42
44
  events:
43
45
  '@sign-out -> function()':
44
46
  desc: Dispara quando o botão de "sair" é clicado.
@@ -1,23 +1,30 @@
1
1
  <template>
2
- <q-drawer v-model="model" :behavior="behavior" bordered class="qas-app-menu" :width="288">
3
- <div class="column flex full-height justify-between no-wrap overflow-x-hidden">
4
- <div class="q-mt-lg">
5
- <div v-if="displayModuleSection" class="q-mb-lg q-mx-md">
6
- <div class="q-mb-sm text-caption text-grey-7 text-weight-medium">
7
- Você está em:
8
- </div>
9
-
10
- <qas-select v-model="module" :options="defaultModules" @update:model-value="redirectHandler(currentModelOption)" />
2
+ <q-drawer v-model="model" :behavior="behavior" class="qas-app-menu" :width="280">
3
+ <div class="column full-height justify-between">
4
+ <div>
5
+ <!-- Brand -->
6
+ <div v-if="!$qas.screen.untilLarge" class="q-pt-xl q-px-lg">
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">
9
+ <span v-else class="ellipsis text-bold text-primary">{{ title }}</span>
10
+ <q-badge color="red" floating :label="developmentBadgeLabel" />
11
+ </router-link>
11
12
  </div>
12
13
 
13
- <q-list v-if="items.length" class="q-mb-lg text-grey-9">
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 rounded-borders shadow-2" dense input-class="q-px-md" :options="defaultModules" :outlined="false" @update:model-value="redirectHandler(currentModelOption)" />
17
+ </div>
18
+
19
+ <!-- Menu -->
20
+ <q-list v-if="items.length" class="q-mt-xl qas-app-menu__menu text-grey-9">
14
21
  <template v-for="(menuItem, index) in items">
15
- <div v-if="hasChildren(menuItem)" :key="`children-${index}`" class="qas-app-menu__content">
16
- <q-item class="items-center">
22
+ <div v-if="hasChildren(menuItem)" :key="`children-${index}`">
23
+ <q-item class="items-center q-pb-none q-pt-md text-weight-bold">
17
24
  {{ menuItem.label }}
18
25
  </q-item>
19
26
 
20
- <q-item v-for="(menuChildItem, childIndex) in menuItem.children" :key="childIndex" :active="isActive(menuChildItem)" class="qas-app-menu__children qas-app-menu__item-children" :to="getRouterRedirect(menuChildItem)">
27
+ <q-item v-for="(menuChildItem, childIndex) in menuItem.children" :key="childIndex" :active="isActive(menuChildItem)" :to="getRouterRedirect(menuChildItem)">
21
28
  <q-item-section v-if="menuChildItem.icon" avatar>
22
29
  <q-icon :name="menuChildItem.icon" />
23
30
  </q-item-section>
@@ -28,7 +35,7 @@
28
35
  </q-item>
29
36
  </div>
30
37
 
31
- <q-item v-else :key="index" :active="isActive(menuItem)" active-class="q-router-link--active" class="qas-app-menu__item" :to="getRouterRedirect(menuItem)">
38
+ <q-item v-else :key="index" :active="isActive(menuItem)" active-class="q-router-link--active" :to="getRouterRedirect(menuItem)">
32
39
  <q-item-section v-if="menuItem.icon" avatar>
33
40
  <q-icon :name="menuItem.icon" />
34
41
  </q-item-section>
@@ -40,17 +47,32 @@
40
47
  </template>
41
48
  </q-list>
42
49
  </div>
50
+
51
+ <!-- User -->
52
+ <div v-if="showUser" class="full-width q-pb-lg q-px-lg">
53
+ <qas-app-user avatar-size="48px" :user="user" @sign-out="signOut" />
54
+ </div>
43
55
  </div>
44
56
  </q-drawer>
45
57
  </template>
46
58
 
47
59
  <script>
60
+ import QasAppUser from '../app-user/QasAppUser.vue'
48
61
  import { isLocalDevelopment } from '../../helpers'
49
62
 
50
63
  export default {
51
64
  name: 'QasAppMenu',
52
65
 
66
+ components: {
67
+ QasAppUser
68
+ },
69
+
53
70
  props: {
71
+ brand: {
72
+ default: '',
73
+ type: String
74
+ },
75
+
54
76
  items: {
55
77
  default: () => [],
56
78
  type: Array
@@ -61,18 +83,30 @@ export default {
61
83
  type: Boolean
62
84
  },
63
85
 
86
+ modules: {
87
+ default: () => [],
88
+ type: Array
89
+ },
90
+
91
+ notifications: {
92
+ default: () => ({}),
93
+ type: Object
94
+ },
95
+
64
96
  title: {
65
97
  default: '',
98
+ required: true,
66
99
  type: String
67
100
  },
68
101
 
69
- modules: {
70
- default: () => [],
71
- type: Array
102
+ user: {
103
+ default: () => ({}),
104
+ require: true,
105
+ type: Object
72
106
  }
73
107
  },
74
108
 
75
- emits: ['update:modelValue'],
109
+ emits: ['sign-out', 'update:modelValue'],
76
110
 
77
111
  data () {
78
112
  return {
@@ -81,6 +115,19 @@ export default {
81
115
  },
82
116
 
83
117
  computed: {
118
+ behavior () {
119
+ return this.$qas.screen.untilLarge ? 'mobile' : 'desktop'
120
+ },
121
+
122
+ currentModelOption () {
123
+ return this.defaultModules.find(module => module?.value === this.module)
124
+ },
125
+
126
+ currentModule () {
127
+ const hostname = window.location.hostname
128
+ return this.defaultModules.find(module => module?.value.includes(hostname))?.value
129
+ },
130
+
84
131
  defaultModules () {
85
132
  if (!isLocalDevelopment()) return this.modules
86
133
 
@@ -88,7 +135,7 @@ export default {
88
135
  const { host, protocol } = window.location
89
136
  const value = `${protocol}//${host}`
90
137
 
91
- // if app is in development mode (local) it's added a default module
138
+ // Add a default module called "Localhost" when app is running in local development.
92
139
  defaultModules.unshift({
93
140
  label: `Localhost ${this.title ? `(${this.title})` : ''}`,
94
141
  value
@@ -97,32 +144,47 @@ export default {
97
144
  return defaultModules
98
145
  },
99
146
 
100
- model: {
101
- get () {
102
- return this.modelValue
103
- },
147
+ developmentBadgeLabel () {
148
+ const hosts = {
149
+ localhost: 'Local',
150
+ '.dev.': 'Develop'
151
+ }
104
152
 
105
- set (value) {
106
- return this.$emit('update:modelValue', value)
153
+ if (process.env.DEV) {
154
+ return hosts.localhost
107
155
  }
108
- },
109
156
 
110
- currentModelOption () {
111
- return this.defaultModules.find(module => module?.value === this.module)
157
+ const current = Object.keys(hosts).find(
158
+ host => location.hostname.includes(host)
159
+ )
160
+
161
+ return current ? hosts[current] : ''
112
162
  },
113
163
 
114
164
  displayModuleSection () {
115
165
  return this.defaultModules.length
116
166
  },
117
167
 
118
- currentModule () {
119
- const hostname = window.location.hostname
168
+ hasDevelopmentBadge () {
169
+ return !!this.developmentBadgeLabel
170
+ },
120
171
 
121
- return this.defaultModules.find(module => module?.value.includes(hostname))?.value
172
+ model: {
173
+ get () {
174
+ return this.modelValue
175
+ },
176
+
177
+ set (value) {
178
+ return this.$emit('update:modelValue', value)
179
+ }
122
180
  },
123
181
 
124
- behavior () {
125
- return this.$qas.screen.untilLarge ? 'mobile' : 'desktop'
182
+ rootRoute () {
183
+ return this.$router.hasRoute('Root') ? { name: 'Root' } : { path: '/' }
184
+ },
185
+
186
+ showUser () {
187
+ return this.hasUser && !this.$qas.screen.untilLarge
126
188
  }
127
189
  },
128
190
 
@@ -131,42 +193,50 @@ export default {
131
193
  handler (value) {
132
194
  this.module = value
133
195
  },
196
+
134
197
  immediate: true
135
198
  }
136
199
  },
137
200
 
138
201
  methods: {
139
- hasChildren ({ children }) {
140
- return !!(children || []).length
141
- },
142
-
143
- redirectHandler ({ value }) {
144
- if (!value.includes(window.location.host)) {
145
- window.location.href = value
146
- }
202
+ getNormalizedPath (path) {
203
+ return path.split('/').filter(Boolean)?.[0]
147
204
  },
148
205
 
149
206
  getPathFromObject ({ path, name }) {
150
207
  if (path) return this.getNormalizedPath(path)
151
208
 
152
209
  const resolvedRoute = this.$router.resolve({ name })
153
-
154
210
  return this.getNormalizedPath(resolvedRoute.path)
155
211
  },
156
212
 
157
- getNormalizedPath (path) {
158
- return path.split('/').filter(Boolean)?.[0]
159
- },
160
-
161
213
  getRouterRedirect ({ to }) {
162
214
  return to || ''
163
215
  },
164
216
 
217
+ hasChildren ({ children }) {
218
+ return !!(children || []).length
219
+ },
220
+
221
+ hasUser () {
222
+ return !!Object.keys(this.user).length
223
+ },
224
+
165
225
  isActive ({ to }) {
166
226
  const currentPath = this.getNormalizedPath(this.$route.path)
167
227
  const itemPath = typeof to === 'string' ? this.getNormalizedPath(to) : this.getPathFromObject(to)
168
228
 
169
229
  return currentPath === itemPath
230
+ },
231
+
232
+ redirectHandler ({ value }) {
233
+ if (!value.includes(window.location.host)) {
234
+ window.location.href = value
235
+ }
236
+ },
237
+
238
+ signOut () {
239
+ this.$emit('sign-out')
170
240
  }
171
241
  }
172
242
  }
@@ -174,28 +244,33 @@ export default {
174
244
 
175
245
  <style lang="scss">
176
246
  .qas-app-menu {
177
- .q-item + .q-item {
178
- margin-top: var(--qas-spacing-xs);
179
- }
180
-
181
- &__children.q-item {
182
- padding-left: var(--qas-spacing-xl);
183
- }
247
+ // Workaround para alterar o padding interno do QSelect sem influenciar na caixa de opções.
248
+ &__module {
249
+ .q-field__native {
250
+ padding-left: var(--qas-spacing-md);
251
+ }
184
252
 
185
- &__item-children.q-item + &__item-children.q-item {
186
- margin-top: var(--qas-spacing-sm);
253
+ .q-field__append {
254
+ margin-right: var(--qas-spacing-sm);
255
+ }
187
256
  }
188
257
 
189
- &__content + &__content {
190
- margin-top: var(--qas-spacing-md);
258
+ &__menu .q-item {
259
+ padding-left: var(--qas-spacing-lg);
191
260
  }
192
261
 
193
- &__content + &__item {
194
- margin-top: var(--qas-spacing-md);
262
+ // User
263
+ .qas-app-user__data {
264
+ line-height: 1.25;
195
265
  }
196
266
 
197
- &__item + &__content {
198
- margin-top: var(--qas-spacing-md);
267
+ // Media: untilLarge
268
+ @media (min-width: $breakpoint-sm-max) {
269
+ // Menu
270
+ &__menu {
271
+ max-height: calc(100vh - 310px);
272
+ overflow-x: auto;
273
+ }
199
274
  }
200
275
  }
201
276
  </style>
@@ -4,6 +4,9 @@ meta:
4
4
  desc: Menu lateral.
5
5
 
6
6
  props:
7
+ brand:
8
+ desc: Path do logotipo.
9
+ type: String
7
10
  items:
8
11
  desc: Itens do menu.
9
12
  type: Array
@@ -19,11 +22,43 @@ props:
19
22
  type: Array
20
23
  default: []
21
24
 
25
+ notifications:
26
+ desc: Lista com as notificações do usuário.
27
+ type: Object
28
+ default: {}
29
+
22
30
  title:
23
31
  desc: Título que vai ficar no label do select de módulos.
32
+ required: true
24
33
  type: String
25
34
 
35
+ user:
36
+ desc: Informações do usuário.
37
+ type: Object
38
+ default: {}
39
+ examples: [
40
+ "
41
+ {
42
+ photo: 'minha-img',
43
+ name: 'Eduardo Lima',
44
+ email: 'eduardolima@gmail.com'
45
+ }
46
+ "
47
+ ]
48
+
49
+ slots:
50
+ user:
51
+ desc: Slot para acessar o menu de usuário.
52
+ scope:
53
+ user:
54
+ desc: Informações do usuário, recebe o objeto "user".
55
+ type: Object
56
+ default: {}
57
+
26
58
  events:
59
+ '@sign-out -> function()':
60
+ desc: Dispara quando o botão de "sair" é clicado.
61
+
27
62
  '@update:model-value -> function(value)':
28
63
  desc: Dispara quando o model-value altera, também usado para v-model.
29
64
  params:
@@ -0,0 +1,141 @@
1
+ <template>
2
+ <div class="cursor-pointer items-center no-wrap q-gutter-sm qas-app-user row">
3
+ <div class="relative-position">
4
+ <qas-avatar color="white" dark :image="user.photo" rounded :size="avatarSize" text-color="primary" :title="userName" />
5
+ <q-badge v-if="hasNotifications" color="red" floating>{{ notifications.count }}</q-badge>
6
+ </div>
7
+
8
+ <div class="ellipsis qas-app-user__data">
9
+ <div class="ellipsis qas-app-user__name text-grey-9">{{ userName }}</div>
10
+ <div class="ellipsis qas-app-user__email text-grey-8">{{ user.email }}</div>
11
+ </div>
12
+
13
+ <q-menu class="shadow-2 text-grey-9" max-height="400px" v-bind="menuProps">
14
+ <div class="q-pb-sm q-pt-md q-px-md qas-app-user__menu">
15
+ <qas-avatar class="q-mb-md" :image="user.photo" size="96px" :title="userName" />
16
+
17
+ <div class="ellipsis qas-app-user__menu-name">{{ userName }}</div>
18
+ <div class="ellipsis">{{ user.email }}</div>
19
+
20
+ <q-list class="q-mt-sm">
21
+ <q-item v-close-popup :active="false" class="qas-app-user__menu-item" clickable :to="user.to">
22
+ <q-item-section avatar>
23
+ <q-icon name="o_person" size="20px" />
24
+ </q-item-section>
25
+
26
+ <q-item-section>Editar</q-item-section>
27
+ </q-item>
28
+
29
+ <q-item v-if="hasNotifications" v-close-popup class="qas-app-user__menu-item" clickable>
30
+ <q-item-section avatar>
31
+ <q-icon name="o_notifications" size="20px" />
32
+ </q-item-section>
33
+
34
+ <q-item-section>Notificações</q-item-section>
35
+
36
+ <q-item-section side>
37
+ <q-badge color="red">{{ notifications.count }}</q-badge>
38
+ </q-item-section>
39
+ </q-item>
40
+
41
+ <q-item v-close-popup class="qas-app-user__menu-item" clickable @click="signOut">
42
+ <q-item-section avatar>
43
+ <q-icon name="o_logout" size="20px" />
44
+ </q-item-section>
45
+
46
+ <q-item-section>Sair</q-item-section>
47
+ </q-item>
48
+ </q-list>
49
+ </div>
50
+ </q-menu>
51
+ </div>
52
+ </template>
53
+
54
+ <script>
55
+ import QasAvatar from '../avatar/QasAvatar.vue'
56
+
57
+ export default {
58
+ name: 'QasAppUser',
59
+
60
+ components: {
61
+ QasAvatar
62
+ },
63
+
64
+ props: {
65
+ avatarSize: {
66
+ default: '36px',
67
+ type: String
68
+ },
69
+
70
+ menuProps: {
71
+ default: () => ({}),
72
+ type: Object
73
+ },
74
+
75
+ notifications: {
76
+ default: () => ({}),
77
+ type: Object
78
+ },
79
+
80
+ user: {
81
+ default: () => ({}),
82
+ required: true,
83
+ type: Object
84
+ }
85
+ },
86
+
87
+ emits: ['sign-out'],
88
+
89
+ computed: {
90
+ hasNotifications () {
91
+ return !!Object.keys(this.notifications).length
92
+ },
93
+
94
+ userName () {
95
+ return this.user.name || this.user.givenName
96
+ }
97
+ },
98
+
99
+ methods: {
100
+ signOut () {
101
+ this.$emit('sign-out')
102
+ }
103
+ }
104
+ }
105
+ </script>
106
+
107
+ <style lang="scss">
108
+ .qas-app-user {
109
+ &__data {
110
+ line-height: 1.1;
111
+ }
112
+
113
+ &__name,
114
+ &__menu-name {
115
+ font-weight: 600;
116
+ }
117
+
118
+ &__email {
119
+ font-size: 0.75rem;
120
+ }
121
+
122
+ &__menu {
123
+ width: 248px;
124
+ }
125
+
126
+ &__menu-name {
127
+ font-size: 1.125rem;
128
+ }
129
+
130
+ &__menu-item {
131
+ min-height: 36px;
132
+ padding: 0;
133
+ }
134
+
135
+ @media (max-width: $breakpoint-xs) {
136
+ &__data {
137
+ display: none;
138
+ }
139
+ }
140
+ }
141
+ </style>
@@ -0,0 +1,29 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Exibe o avatar e o nome do usuário, ao clicar abre um menu com as opções.
5
+
6
+ props:
7
+ notifications:
8
+ desc: Lista com as notificações do usuário.
9
+ type: Object
10
+ default: {}
11
+
12
+ user:
13
+ desc: Informações do usuário.
14
+ required: true
15
+ type: Object
16
+ default: {}
17
+ examples: [
18
+ "
19
+ {
20
+ photo: 'minha-img',
21
+ name: 'Eduardo Lima',
22
+ email: 'eduardolima@gmail.com'
23
+ }
24
+ "
25
+ ]
26
+
27
+ events:
28
+ '@sign-out -> function()':
29
+ desc: Dispara quando o botão de "sair" é clicado.
@@ -92,6 +92,13 @@ export default {
92
92
 
93
93
  id () {
94
94
  return this.customId || this.$route.params.id
95
+ },
96
+
97
+ defaultNotifyMessages () {
98
+ return {
99
+ error: 'Não conseguimos excluir as informações. Por favor, tente novamente em alguns minutos.',
100
+ success: 'Informações excluídas com sucesso.'
101
+ }
95
102
  }
96
103
  },
97
104
 
@@ -105,8 +112,6 @@ export default {
105
112
  this.$emit('update:deleting', true)
106
113
 
107
114
  try {
108
- const { destroyRoutes, history } = useHistory()
109
-
110
115
  const payload = { id: this.id, url: this.url }
111
116
 
112
117
  this.$qas.logger.group(
@@ -119,9 +124,11 @@ export default {
119
124
  payload
120
125
  })
121
126
 
122
- NotifySuccess('Item deletado com sucesso!')
127
+ NotifySuccess(this.defaultNotifyMessages.success)
123
128
 
124
129
  if (this.useAutoDeleteRoute) {
130
+ const { destroyRoutes, history } = useHistory()
131
+
125
132
  // remove todas rotas que possuem o id do item excluído.
126
133
  const routesToBeDeleted = this.getRoutesToBeDeletedById(history.list)
127
134
  destroyRoutes(routesToBeDeleted)
@@ -134,7 +141,7 @@ export default {
134
141
 
135
142
  this.$qas.logger.info('QasDelete - destroy -> item deletado com sucesso!')
136
143
  } catch (error) {
137
- NotifyError('Ops! Não foi possível deletar o item.')
144
+ NotifyError(this.defaultNotifyMessages.error)
138
145
  this.$emit('error', error)
139
146
 
140
147
  this.$qas.logger.group(
@@ -58,7 +58,7 @@ export default {
58
58
 
59
59
  props: {
60
60
  cancelButtonLabel: {
61
- default: 'Cancelar',
61
+ default: 'Voltar',
62
62
  type: String
63
63
  },
64
64
 
@@ -196,6 +196,14 @@ export default {
196
196
 
197
197
  isCancelButtonDisabled () {
198
198
  return this.disable || this.isSubmitting
199
+ },
200
+
201
+ defaultNotifyMessages () {
202
+ return {
203
+ validationError: 'Existem campos no formulário que ainda não foram preenchidos. Complete todas as informações para avançar.',
204
+ error: 'Não conseguimos salvar as informações. Por favor, tente novamente em alguns minutos.',
205
+ success: 'Informações salvas com sucesso.'
206
+ }
199
207
  }
200
208
  },
201
209
 
@@ -420,7 +428,7 @@ export default {
420
428
  }
421
429
 
422
430
  this.mx_setErrors()
423
- NotifySuccess(response.data.status.text || 'Item salvo com sucesso!')
431
+ NotifySuccess(response.data.status.text || this.defaultNotifyMessages.success)
424
432
  this.$emit('submit-success', response, this.modelValue)
425
433
 
426
434
  this.$qas.logger.group(
@@ -429,16 +437,15 @@ export default {
429
437
  } catch (error) {
430
438
  const errors = error?.response?.data?.errors
431
439
  const message = error?.response?.data?.status?.text
432
- const exceptionResponse = error?.response?.data?.exception
433
440
 
434
- const exception = errors
435
- ? 'Existem erros de validação no formulário.'
436
- : exceptionResponse || error.message
441
+ const defaultMessage = error
442
+ ? this.defaultNotifyMessages.validationError
443
+ : this.defaultNotifyMessages.error
437
444
 
438
445
  this.mx_setErrors(errors)
439
446
  this.$emit('update:errors', this.mx_errors)
440
447
 
441
- NotifyError(message || 'Ops! Erro ao salvar item.', exception)
448
+ NotifyError(message || defaultMessage)
442
449
 
443
450
  this.$emit('submit-error', error)
444
451
 
@@ -17,8 +17,8 @@ props:
17
17
  examples: ['beforeSubmit({ payload, resolve })']
18
18
 
19
19
  cancel-button-label:
20
- desc: Rótulo do botão "cancelar".
21
- default: Cancelar
20
+ desc: Rótulo do botão "voltar".
21
+ default: Voltar
22
22
  type: String
23
23
 
24
24
  cancel-route:
@@ -1,18 +1,22 @@
1
1
  <template>
2
2
  <q-layout view="hHh Lpr lFf">
3
- <slot name="app-bar">
4
- <qas-app-bar v-bind="appBarProps" @toggle-menu="toggleMenuDrawer" />
3
+ <slot v-if="$qas.screen.untilLarge" name="app-bar">
4
+ <qas-app-bar v-bind="appBarProps" @sign-out="signOut" @toggle-menu="toggleMenuDrawer" />
5
5
  </slot>
6
6
 
7
7
  <slot name="app-menu">
8
- <qas-app-menu v-model="menuDrawer" v-bind="defaultAppMenuProps" />
8
+ <qas-app-menu :model-value="showMenuDrawer" v-bind="defaultAppMenuProps" @sign-out="signOut" @update:model-value="updateMenuDrawer" />
9
9
  </slot>
10
10
 
11
11
  <slot>
12
12
  <q-page-container>
13
- <router-view />
13
+ <q-page padding>
14
+ <router-view />
15
+ </q-page>
14
16
  </q-page-container>
15
17
  </slot>
18
+
19
+ <q-ajax-bar color="primary" position="bottom" size="2px" />
16
20
  </q-layout>
17
21
  </template>
18
22
 
@@ -45,42 +49,39 @@ export default {
45
49
  }
46
50
  },
47
51
 
48
- emits: ['update:modelValue'],
52
+ emits: ['sign-out', 'update:modelValue'],
49
53
 
50
54
  data () {
51
55
  return {
52
- menuDrawer: true
56
+ menuDrawer: false
53
57
  }
54
58
  },
55
59
 
56
60
  computed: {
57
61
  defaultAppMenuProps () {
58
62
  return {
59
- ...this.appMenuProps,
60
- title: this.appBarProps?.title
63
+ ...this.appBarProps,
64
+ ...this.appMenuProps
61
65
  }
62
- }
63
- },
64
-
65
- watch: {
66
- modelValue: {
67
- handler (value) {
68
- if (!this.$qas.screen.untilLarge) return
66
+ },
69
67
 
70
- this.menuDrawer = value
71
- },
72
- immediate: true
68
+ showMenuDrawer () {
69
+ return !this.$qas.screen.untilLarge || this.menuDrawer
73
70
  }
74
71
  },
75
72
 
76
- mounted () {
77
- this.menuDrawer = !this.$qas.screen.untilLarge
78
- },
79
-
80
73
  methods: {
74
+ signOut () {
75
+ this.$emit('sign-out')
76
+ },
77
+
81
78
  toggleMenuDrawer () {
82
- this.menuDrawer = !this.menuDrawer
83
- this.$emit('update:modelValue', this.menuDrawer)
79
+ this.updateMenuDrawer(!this.menuDrawer)
80
+ },
81
+
82
+ updateMenuDrawer (value) {
83
+ this.menuDrawer = value
84
+ this.$emit('update:modelValue', value)
84
85
  }
85
86
  }
86
87
  }
@@ -30,6 +30,9 @@ slots:
30
30
  desc: Slot para substituir o "QPageContainer".
31
31
 
32
32
  events:
33
+ '@sign-out -> function()':
34
+ desc: Dispara quando o botão de "sair" é clicado.
35
+
33
36
  '@update:model-value -> function(value)':
34
37
  desc: Dispara quando o model-value altera, também usado para v-model.
35
38
  params:
@@ -281,8 +281,8 @@ export default {
281
281
  }
282
282
  },
283
283
 
284
- showDestroyBtn () {
285
- return this.nested.filter(item => !item[this.destroyKey]).length > 1 || this.useDestroyAlways
284
+ showDestroyButton () {
285
+ return this.nested.filter(item => !item[this.destroyKey]).length > 1 || this.hasDestroyAlways
286
286
  },
287
287
 
288
288
  transformedErrors () {
@@ -329,7 +329,7 @@ export default {
329
329
  }
330
330
  }
331
331
 
332
- if (this.showDestroyBtn) {
332
+ if (this.showDestroyButton) {
333
333
  list.destroy = {
334
334
  ...this.buttonDestroyProps,
335
335
  handler: () => this.destroy(index, row)
@@ -2,7 +2,6 @@
2
2
  <q-toolbar class="justify-between q-mb-lg q-px-none">
3
3
  <div class="ellipsis">
4
4
  <q-toolbar-title v-if="title" class="text-bold text-h5">
5
- <q-icon v-if="hasPreviousRoute" class="cursor-pointer vertical-baseline" name="o_arrow_back" size="18px" @click="back" />
6
5
  {{ title }}
7
6
  </q-toolbar-title>
8
7
 
@@ -19,7 +18,7 @@ import { castArray } from 'lodash-es'
19
18
  import { useHistory } from '../../composables'
20
19
  import { createMetaMixin } from 'quasar'
21
20
 
22
- const { hasPreviousRoute, history, getPreviousRoute } = useHistory()
21
+ const { history } = useHistory()
23
22
 
24
23
  export default {
25
24
  name: 'QasPageHeader',
@@ -55,10 +54,6 @@ export default {
55
54
  },
56
55
 
57
56
  computed: {
58
- hasPreviousRoute () {
59
- return hasPreviousRoute.value
60
- },
61
-
62
57
  transformedBreadcrumbs () {
63
58
  const list = [...castArray(this.breadcrumbs || this.title)]
64
59
  this.root && list.unshift(this.root)
@@ -85,10 +80,6 @@ export default {
85
80
  },
86
81
 
87
82
  methods: {
88
- back () {
89
- this.$router.push(getPreviousRoute(this.$route))
90
- },
91
-
92
83
  getBreadcrumbsClass (index) {
93
84
  const lastIndex = this.transformedBreadcrumbs.length - 1
94
85
 
@@ -1,9 +1,7 @@
1
1
  <template>
2
2
  <q-select v-model="model" v-bind="attributes">
3
- <template #append>
4
- <slot name="append">
5
- <q-icon v-if="isSearchable" name="o_search" />
6
- </slot>
3
+ <template v-if="isSearchable" #prepend>
4
+ <q-icon name="o_search" />
7
5
  </template>
8
6
 
9
7
  <template #no-option>
@@ -1,5 +1,6 @@
1
1
  .q-notification {
2
2
  margin-top: 80px;
3
+ max-width: 560px;
3
4
 
4
5
  &__content {
5
6
  .q-icon {
package/src/index.scss CHANGED
@@ -9,7 +9,7 @@ $accent: var(--q-accent);
9
9
 
10
10
  // Asteroid variables
11
11
  :root {
12
- --qas-background-color: #f5f5f5;
12
+ --qas-background-color: #fbfbfb;
13
13
  --qas-generic-border-radius: 8px;
14
14
  }
15
15
 
@@ -81,13 +81,16 @@ export default {
81
81
 
82
82
  mx_hasHeaderSlot () {
83
83
  return !!(this.$slots.header)
84
+ },
85
+
86
+ mx_fetchErrorMessage () {
87
+ return 'Ops… Não conseguimos acessar as informações. Por favor, tente novamente em alguns minutos.'
84
88
  }
85
89
  },
86
90
 
87
91
  methods: {
88
92
  mx_fetchError (error) {
89
93
  const { response } = error
90
- const exception = response?.data?.exception || error.message
91
94
 
92
95
  const status = response?.status
93
96
 
@@ -102,7 +105,7 @@ export default {
102
105
  return
103
106
  }
104
107
 
105
- this.$qas.error('Ops! Erro ao obter os dados.', exception)
108
+ this.$qas.error(this.mx_fetchErrorMessage)
106
109
  },
107
110
 
108
111
  mx_setErrors (errors = {}) {
package/src/vue-plugin.js CHANGED
@@ -3,6 +3,7 @@ import QasActionsMenu from './components/actions-menu/QasActionsMenu.vue'
3
3
  import QasAlert from './components/alert/QasAlert.vue'
4
4
  import QasAppBar from './components/app-bar/QasAppBar.vue'
5
5
  import QasAppMenu from './components/app-menu/QasAppMenu.vue'
6
+ import QasAppUser from './components/app-user/QasAppUser.vue'
6
7
  import QasAvatar from './components/avatar/QasAvatar.vue'
7
8
  import QasBox from './components/box/QasBox.vue'
8
9
  import QasBreakline from './components/breakline/QasBreakline.vue'
@@ -75,6 +76,7 @@ function install (app) {
75
76
  app.component('QasAlert', QasAlert)
76
77
  app.component('QasAppBar', QasAppBar)
77
78
  app.component('QasAppMenu', QasAppMenu)
79
+ app.component('QasAppUser', QasAppUser)
78
80
  app.component('QasAvatar', QasAvatar)
79
81
  app.component('QasBox', QasBox)
80
82
  app.component('QasBreakline', QasBreakline)
@@ -148,6 +150,7 @@ export {
148
150
  QasAlert,
149
151
  QasAppBar,
150
152
  QasAppMenu,
153
+ QasAppUser,
151
154
  QasAvatar,
152
155
  QasBox,
153
156
  QasBreakline,