@bildvitta/quasar-ui-asteroid 3.18.0-beta.2 → 3.18.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bildvitta/quasar-ui-asteroid",
3
3
  "description": "Asteroid",
4
- "version": "3.18.0-beta.2",
4
+ "version": "3.18.0-beta.4",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "dist/asteroid.cjs.min.js",
@@ -1,11 +1,12 @@
1
1
  <template>
2
2
  <div v-if="hasList" class="qas-actions-menu" data-cy="actions-menu">
3
- <qas-btn-dropdown v-bind="btnDropdownProps">
4
- <q-list data-cy="actions-menu-list" separator>
3
+ <qas-btn-dropdown v-bind="btnDropdownProps" v-model:menu="menuModel">
4
+ <q-list v-if="hasDropdownLength" data-cy="actions-menu-list">
5
5
  <slot v-for="(item, key) in formattedList.dropdownList" :item="item" :name="key">
6
6
  <q-item v-bind="getItemProps(item)" :key="key" active-class="primary" clickable data-cy="actions-menu-list-item" @click="setClickHandler(item)">
7
7
  <q-item-section avatar>
8
- <q-icon :name="item.icon" />
8
+ <q-spinner v-if="item.loading" size="sm" />
9
+ <q-icon v-else :name="item.icon" />
9
10
  </q-item-section>
10
11
 
11
12
  <q-item-section>
@@ -41,7 +42,7 @@ import useSingleSplitActions from './composables/use-single-split-actions'
41
42
  import getLabel from './utils/get-label'
42
43
  import setClickHandler from './utils/set-click-handler'
43
44
 
44
- import { computed, inject } from 'vue'
45
+ import { computed, inject, ref, watch } from 'vue'
45
46
 
46
47
  const DEFAULT_COLOR = 'grey-10'
47
48
  const SPLIT_SIZE = 2
@@ -95,10 +96,15 @@ const props = defineProps({
95
96
  }
96
97
  })
97
98
 
99
+ // refs
100
+ const menuModel = ref(false)
101
+
102
+ // composables
98
103
  const screen = useScreen()
99
104
 
100
105
  const { deleteBtnProps, hasDelete } = useDelete({ color: DEFAULT_COLOR, props, qas })
101
106
 
107
+ // computeds
102
108
  const hasSplitName = computed(() => !!props.splitName)
103
109
  const hasList = computed(() => !!Object.keys(fullList.value).length)
104
110
 
@@ -157,11 +163,22 @@ const defaultButtonPropsList = computed(() => {
157
163
  return normalizedButtonPropsList
158
164
  })
159
165
 
166
+ /**
167
+ * Procura se alguma ação está com "loading: true", para o dropdown não fechar
168
+ * automaticamente após clicar em alguma ação.
169
+ */
170
+ const hasActiveLoading = computed(() => {
171
+ return Object.values(formattedList.value.dropdownList)?.some(action => action.loading)
172
+ })
173
+
174
+ const hasDropdownLength = computed(() => !!Object.keys(formattedList.value.dropdownList).length)
175
+
160
176
  const btnDropdownProps = computed(() => {
161
177
  return {
162
178
  buttonsPropsList: defaultButtonPropsList.value,
163
179
  disable: props.disable,
164
- useSplit: hasSplit.value
180
+ useSplit: hasSplit.value,
181
+ useAutoClose: !hasActiveLoading.value
165
182
  }
166
183
  })
167
184
 
@@ -238,14 +255,22 @@ const formattedList = computed(() => {
238
255
 
239
256
  const { showTooltip, tooltipLabels } = useTooltips({ formattedList, fullList, props })
240
257
 
258
+ watch(() => hasActiveLoading.value, handleMenuModel)
241
259
  // functions
242
260
  function getItemProps (item) {
243
- const { disable, props: itemProps, to } = item
261
+ const { disable, loading, props: itemProps, to } = item
244
262
 
245
263
  return {
246
- disable,
264
+ disable: disable || loading, // ficará desabilitado se for passado a prop "disable" ou o "loading" seja true.
247
265
  to,
248
266
  ...itemProps
249
267
  }
250
268
  }
269
+
270
+ function handleMenuModel (newValue, oldValue) {
271
+ // Fecha o menu após o estado de loading do item passar de true para false
272
+ if (oldValue && !newValue) {
273
+ menuModel.value = false
274
+ }
275
+ }
251
276
  </script>
@@ -61,7 +61,7 @@ const props = defineProps({
61
61
 
62
62
  text: {
63
63
  type: String,
64
- required: true
64
+ default: ''
65
65
  },
66
66
 
67
67
  useBox: {
@@ -83,17 +83,17 @@
83
83
  </q-list>
84
84
 
85
85
  <!-- usuário + chat ajuda -->
86
- <div v-if="showAppUser" class="q-mt-auto" @mouseenter="onMouseEvent" @mouseleave="onMouseEvent">
86
+ <div v-if="showAppUser" class="column justify-end no-wrap qas-app-menu__user-chat" @mouseenter="onMouseEvent" @mouseleave="onMouseEvent">
87
87
  <!-- Chat Ajuda -->
88
88
  <q-list v-if="useChat" class="q-mt-md">
89
- <q-item class="q-pb-none" clickable @click="toggleChat">
90
- <q-item-section avatar>
91
- <q-icon color="primary" name="sym_r_chat" />
89
+ <q-item class="q-pb-none qas-app-menu__chat-item" clickable @click="toggleChat">
90
+ <q-item-section avatar class="qas-app-menu__chat-item-section text-primary">
91
+ <q-icon name="sym_r_chat" />
92
92
  </q-item-section>
93
93
 
94
- <q-item-section>
94
+ <q-item-section class="qas-app-menu__chat-item-section text-primary">
95
95
  <q-item-label>
96
- <div class="ellipsis text-primary text-subtitle2">
96
+ <div class="ellipsis text-subtitle2">
97
97
  Solicitar ajuda
98
98
  </div>
99
99
  </q-item-label>
@@ -478,6 +478,19 @@ function useChatMenu () {
478
478
  padding-right: 0 !important;
479
479
  }
480
480
 
481
+ // Faz com que essa área ocupe todo o tamanho restante até o QList, adicionando o evento de mouseover.
482
+ &__user-chat {
483
+ flex: 1 1 auto;
484
+ }
485
+
486
+ &__chat-item:hover &__chat-item-section {
487
+ color: var(--qas-primary-contrast) !important;
488
+ }
489
+
490
+ &__chat-item-section {
491
+ transition: color var(--qas-generic-transition);
492
+ }
493
+
481
494
  .q-item:not(&__item--label-mini) {
482
495
  padding-left: var(--qas-spacing-xl) !important;
483
496
  padding-right: var(--qas-spacing-xl) !important;
@@ -1,11 +1,25 @@
1
1
  <template>
2
2
  <!-- "data-table-ignore-tr-hover" é para desabilitar o hover do tr no QasTableGenerator -->
3
3
  <q-btn ref="button" class="qas-btn" data-table-ignore-tr-hover v-bind="attributes">
4
- <slot />
5
-
6
4
  <template v-for="(_, name) in nonDefaultSlots" #[name]="context">
7
5
  <slot :name="name" v-bind="context || {}" />
8
6
  </template>
7
+
8
+ <div class="items-center justify-center no-wrap row text-center" :class="containerClasses">
9
+ <q-spinner v-if="hasLeftSpinner" :class="iconClasses" size="sm" />
10
+
11
+ <q-icon v-if="hasIcon" :class="iconClasses" :name="props.icon" />
12
+
13
+ <div v-if="showLabel" :class="labelClasses">
14
+ {{ props.label }}
15
+ </div>
16
+
17
+ <q-spinner v-if="hasRightSpinner" :class="iconRightClasses" size="sm" />
18
+
19
+ <q-icon v-if="hasIconRight" :class="iconRightClasses" :name="props.iconRight" />
20
+ </div>
21
+
22
+ <slot />
9
23
  </q-btn>
10
24
  </template>
11
25
 
@@ -46,6 +60,10 @@ const props = defineProps({
46
60
  type: String
47
61
  },
48
62
 
63
+ loading: {
64
+ type: Boolean
65
+ },
66
+
49
67
  variant: {
50
68
  default: 'tertiary',
51
69
  type: String,
@@ -84,6 +102,18 @@ const hasIconOnly = computed(() => {
84
102
  )
85
103
  })
86
104
 
105
+ const hasLeftSpinner = computed(() => props.loading && !props.iconRight)
106
+ const hasRightSpinner = computed(() => props.loading && props.iconRight)
107
+
108
+ const hasIconRight = computed(() => props.iconRight && !props.loading)
109
+ const hasIcon = computed(() => props.icon && !props.loading)
110
+
111
+ const labelClasses = computed(() => ({ ellipsis: props.useEllipsis }))
112
+ const containerClasses = computed(() => ({ 'full-width': props.useEllipsis }))
113
+
114
+ const iconClasses = computed(() => ({ 'on-left': !hasIconOnly.value }))
115
+ const iconRightClasses = computed(() => ({ 'on-right': !hasIconOnly.value }))
116
+
87
117
  const classes = computed(() => {
88
118
  return {
89
119
  'qas-btn--primary': isPrimary.value,
@@ -104,17 +134,20 @@ const classes = computed(() => {
104
134
  'qas-btn--no-hover-on-white': !props.useHoverOnWhiteColor,
105
135
 
106
136
  // ellipsis
107
- 'qas-btn--ellipsis': props.useEllipsis
137
+ 'full-width': props.useEllipsis
108
138
  }
109
139
  })
110
140
 
111
141
  const attributes = computed(() => {
112
142
  const {
143
+ class: externalClass,
113
144
  dense,
145
+ disable,
114
146
  fab,
115
147
  fabMini,
116
148
  flat,
117
149
  glossy,
150
+ loading,
118
151
  noWrap,
119
152
  outline,
120
153
  padding,
@@ -127,16 +160,12 @@ const attributes = computed(() => {
127
160
  stretch,
128
161
  textColor,
129
162
  unelevated,
130
- class: externalClass,
131
163
  ...attributesPayload
132
164
  } = attrs
133
165
 
134
166
  return {
135
- ...(showLabel.value && { label: props.label }),
136
-
137
167
  ...attributesPayload,
138
- icon: props.icon,
139
- iconRight: props.iconRight,
168
+ disable: disable || props.loading,
140
169
  class: [classes.value, externalClass]
141
170
  }
142
171
  })
@@ -4,7 +4,7 @@
4
4
  <div v-for="(buttonProps, key, index) in props.buttonsPropsList" :key="key">
5
5
  <div class="flex no-wrap">
6
6
  <qas-btn :disable="props.disable" v-bind="buttonProps" no-wrap variant="tertiary" @click="onClick">
7
- <q-menu v-if="hasMenuOnLeftSide" v-model="isMenuOpened" anchor="bottom right" auto-close self="top right" @update:model-value="onUpdateMenuValue">
7
+ <q-menu v-if="hasMenuOnLeftSide" v-model="isMenuOpened" anchor="bottom right" :auto-close="props.useAutoClose" class="qas-menu" self="top right" @update:model-value="onUpdateMenuValue">
8
8
  <div :class="classes.menuContent">
9
9
  <slot />
10
10
  </div>
@@ -20,7 +20,7 @@
20
20
 
21
21
  <div v-if="props.useSplit">
22
22
  <qas-btn v-bind="splittedButtonProps">
23
- <q-menu v-if="hasDefaultSlot" anchor="bottom right" auto-close self="top right">
23
+ <q-menu v-if="hasDefaultSlot" v-model="isMenuOpened" anchor="bottom right" :auto-close="props.useAutoClose" class="qas-menu" self="top right" @update:model-value="onUpdateMenuValue">
24
24
  <div :class="classes.menuContent">
25
25
  <slot />
26
26
  </div>
@@ -70,6 +70,11 @@ const props = defineProps({
70
70
  useTooltip: {
71
71
  type: Boolean,
72
72
  default: true
73
+ },
74
+
75
+ useAutoClose: {
76
+ type: Boolean,
77
+ default: true
73
78
  }
74
79
  })
75
80
 
@@ -36,6 +36,11 @@ props:
36
36
  default: false
37
37
  type: Boolean
38
38
 
39
+ use-auto-close:
40
+ desc: Controla se o comportamento de click deve fechar o menu.
41
+ default: true
42
+ type: Boolean
43
+
39
44
  slots:
40
45
  default:
41
46
  desc: Slot para passar o conteúdo do dropdown (menu).
@@ -20,7 +20,7 @@
20
20
  </div>
21
21
  </div>
22
22
 
23
- <div class="items-start no-wrap q-col-gutter-sm row" :class="descriptionSectionClasses">
23
+ <div v-if="hasDescriptionOrOnlyActionsSection" class="items-start no-wrap q-col-gutter-sm row" :class="descriptionSectionClasses">
24
24
  <div v-if="hasDescriptionSection" class="text-body1 text-grey-8">
25
25
  <slot name="description">
26
26
  {{ props.description }}
@@ -147,6 +147,15 @@ const hasLabel = computed(() => !!Object.keys(props.labelProps).length)
147
147
  const hasDefaultButton = computed(() => !!Object.keys(props.buttonProps).length)
148
148
  const hasDefaultFilters = computed(() => !!Object.keys(props.filtersProps).length)
149
149
  const hasDefaultActionsMenu = computed(() => !!Object.keys(props.actionsMenuProps).length)
150
- const hasDescriptionSection = computed(() => props.description || slots.description)
150
+ const hasDescriptionSection = computed(() => !!props.description || !!slots.description)
151
151
  const hasLabelSection = computed(() => hasLabel.value || slots.label || hasBadges.value)
152
+
153
+ /**
154
+ * Só exibo a seção de descrição com a seção de ações ao lado quando:
155
+ * - Tenha descrição;
156
+ * - OU não tenha seção da label E tenha componente de ações.
157
+ */
158
+ const hasDescriptionOrOnlyActionsSection = computed(() => {
159
+ return hasDescriptionSection.value || (!hasLabelSection.value && hasActionsComponent.value)
160
+ })
152
161
  </script>
@@ -9,16 +9,6 @@
9
9
  <slot :name="name" v-bind="context" />
10
10
  </template>
11
11
 
12
- <template #header-cell="context">
13
- <q-th v-if="context.col.label" :class="[context.col.headerClasses, context.col.__thClass]">
14
- <qas-btn v-if="context.col.sortable" color="grey-10" icon-right="sym_r_swap_vert" :label="context.col.label" @click="$refs.table.sort(context.col)" />
15
-
16
- <span v-else>
17
- {{ context.col.label }}
18
- </span>
19
- </q-th>
20
- </template>
21
-
22
12
  <template v-for="(fieldName, index) in bodyCellNameSlots" :key="index" #[`body-cell-${fieldName}`]="context">
23
13
  <q-td :class="getTdClasses(context.row)">
24
14
  <component :is="tdChildComponent" class="qas-table-generator__td-item" v-bind="getTdChildComponentProps(context.row)">
@@ -200,17 +190,13 @@ export default {
200
190
  const columns = []
201
191
 
202
192
  function columnByField (field) {
203
- const { label, name, sortable, sort, rawSort } = field
193
+ const { label, name } = field
204
194
 
205
195
  columns.push({
206
196
  align: 'left',
207
197
  field: name,
208
198
  label,
209
- name,
210
- headerClasses: 'text-grey-10',
211
- sortable: sortable ?? true,
212
- sort,
213
- rawSort
199
+ name
214
200
  })
215
201
  }
216
202
 
@@ -502,6 +488,8 @@ export default {
502
488
  padding-bottom: var(--qas-spacing-sm);
503
489
  padding-left: 0;
504
490
  padding-top: var(--qas-spacing-sm);
491
+ position: relative;
492
+ z-index: 0;
505
493
 
506
494
  &:not(:last-child) {
507
495
  padding-right: var(--qas-spacing-md);
@@ -512,40 +500,33 @@ export default {
512
500
  }
513
501
 
514
502
  &::before {
515
- display: none;
503
+ position: absolute;
504
+ content: '';
505
+ top: 0;
506
+ left: 0;
507
+ z-index: -1;
508
+ background-color: transparent;
516
509
  }
517
- }
518
510
 
519
- &__middle {
520
- padding-left: var(--qas-spacing-md);
521
- padding-right: var(--qas-spacing-md);
522
- }
523
-
524
- tr {
525
- position: relative;
526
-
527
- &::before {
528
- background-color: transparent;
529
- bottom: 0;
530
- content: '';
511
+ &:first-child::before {
531
512
  left: calc(var(--qas-spacing-md) * -1);
532
- pointer-events: none;
533
- position: absolute;
513
+ }
514
+
515
+ &:last-child::before {
534
516
  right: calc(var(--qas-spacing-md) * -1);
535
- top: 0;
536
- transition: background-color var(--qas-generic-transition);
537
517
  }
538
518
  }
539
519
 
540
- tbody tr {
541
- &::before {
542
- display: block;
543
- }
520
+ tr:hover td::before {
521
+ background-color: var(--qas-background-color);
522
+ }
544
523
 
545
- &:hover::before {
546
- background-color: var(--qas-background-color);
547
- }
524
+ &__middle {
525
+ padding-left: var(--qas-spacing-md);
526
+ padding-right: var(--qas-spacing-md);
527
+ }
548
528
 
529
+ tbody tr {
549
530
  /*
550
531
  A regra só é aplicada se nenhum elemento filho com o atributo "data-table-ignore-tr-hover"
551
532
  estiver também em estado de hover, impedindo que estilos conflitantes sejam aplicados.
@@ -71,7 +71,7 @@ const component = computed(() => {
71
71
  maxWidth: 260,
72
72
 
73
73
  // caso personalize o componente passando "list", não pode enviar "text" senão vai ter erro de tipo
74
- text: props.componentData.props?.list?.length ? '' : defaultValue
74
+ text: 'list' in (props.componentData.props || {}) ? '' : defaultValue
75
75
  }
76
76
  },
77
77
 
@@ -83,11 +83,23 @@ const component = computed(() => {
83
83
  }
84
84
  }
85
85
 
86
+ // Os componentes abaixo precisam adicionar o stopPropagation e preventDefault no click para nao chamar o rowClick ou rowRouteFn
87
+ const hasPreventEvent = ['QasActionsMenu', 'QasBtn'].includes(props.componentData.component)
88
+
86
89
  return {
87
90
  is: defineAsyncComponent(componentPaths[props.componentData.component].component),
88
91
  props: {
89
92
  ...componentPaths[props.componentData.component].props,
90
- ...props.componentData.props
93
+ ...props.componentData.props,
94
+
95
+ ...(hasPreventEvent && {
96
+ onClick: event => {
97
+ event.stopPropagation()
98
+ event.preventDefault()
99
+
100
+ props.componentData.props?.onClick?.(event)
101
+ }
102
+ })
91
103
  }
92
104
  }
93
105
  })
@@ -10,8 +10,8 @@
10
10
  <span v-if="hasMenuButton(node)" class="q-ml-sm">
11
11
  <!-- TODO: rever para o uso QasActionsMenu -->
12
12
  <qas-btn color="grey-10" icon="sym_r_more_vert" variant="tertiary" @click.stop.prevent>
13
- <q-menu auto-close>
14
- <q-list separator>
13
+ <q-menu auto-close class="qas-menu">
14
+ <q-list>
15
15
  <q-item v-if="useAddButton" v-ripple class="qas-tree-generator__item" clickable @click="handleTreeFormDialog(node, true, tree)">
16
16
  <q-item-section avatar>
17
17
  <q-icon name="sym_r_add_circle_outline" />
@@ -17,7 +17,6 @@
17
17
 
18
18
  <!-- ------------------------------------ tags hidden -------------------------------------- -->
19
19
  <input ref="hiddenInput" :accept="attributes.accept" class="qas-uploader__input" :multiple="isMultiple" type="file">
20
- <qas-btn ref="buttonCleanFiles" class="hidden" @click="scope.removeUploadedFiles" />
21
20
  </slot>
22
21
  </template>
23
22
 
@@ -337,13 +336,13 @@ export default {
337
336
  },
338
337
 
339
338
  dispatchUpload () {
340
- this.$refs.buttonCleanFiles.$el.click()
341
- this.hiddenInputElement.click()
339
+ this.uploader?.removeUploadedFiles?.()
340
+ this.hiddenInputElement?.click?.()
342
341
  },
343
342
 
344
343
  async factory ([file]) {
345
344
  if (!this.isMultiple && !this.hasHeaderSlot) {
346
- this.$refs.buttonCleanFiles.$el.click()
345
+ this.uploader.removeUploadedFiles()
347
346
  }
348
347
 
349
348
  const name = `${uid()}.${file.name.split('.').pop()}`
@@ -101,11 +101,13 @@
101
101
  }
102
102
  }
103
103
 
104
- .q-icon.on-left {
104
+ .q-icon.on-left,
105
+ .q-spinner.on-left {
105
106
  margin-right: var(--qas-spacing-xs);
106
107
  }
107
108
 
108
- .q-icon.on-right {
109
+ .q-icon.on-right,
110
+ .q-spinner.on-right {
109
111
  margin-left: var(--qas-spacing-xs);
110
112
  }
111
113
 
@@ -10,7 +10,7 @@
10
10
  min-height: 36px !important;
11
11
 
12
12
  .q-icon {
13
- font-size: 18px !important;
13
+ font-size: 24px !important;
14
14
  }
15
15
  }
16
16