@bildvitta/quasar-ui-asteroid 3.18.0-beta.3 → 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.3",
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">
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>
@@ -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 class="qas-menu" 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 class="qas-menu" 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)">
@@ -193,8 +183,6 @@ export default {
193
183
  },
194
184
 
195
185
  columnsByFields () {
196
- const hasMultipleResults = this.rowsPerPage > 1
197
-
198
186
  if (!this.hasFields) {
199
187
  return this.normalizedColumns.filter(column => column instanceof Object)
200
188
  }
@@ -202,17 +190,13 @@ export default {
202
190
  const columns = []
203
191
 
204
192
  function columnByField (field) {
205
- const { label, name, sortable, sort, rawSort } = field
193
+ const { label, name } = field
206
194
 
207
195
  columns.push({
208
196
  align: 'left',
209
197
  field: name,
210
198
  label,
211
- name,
212
- headerClasses: 'text-grey-10',
213
- sortable: sortable ?? hasMultipleResults,
214
- sort,
215
- rawSort
199
+ name
216
200
  })
217
201
  }
218
202
 
@@ -504,6 +488,8 @@ export default {
504
488
  padding-bottom: var(--qas-spacing-sm);
505
489
  padding-left: 0;
506
490
  padding-top: var(--qas-spacing-sm);
491
+ position: relative;
492
+ z-index: 0;
507
493
 
508
494
  &:not(:last-child) {
509
495
  padding-right: var(--qas-spacing-md);
@@ -514,40 +500,33 @@ export default {
514
500
  }
515
501
 
516
502
  &::before {
517
- display: none;
503
+ position: absolute;
504
+ content: '';
505
+ top: 0;
506
+ left: 0;
507
+ z-index: -1;
508
+ background-color: transparent;
518
509
  }
519
- }
520
-
521
- &__middle {
522
- padding-left: var(--qas-spacing-md);
523
- padding-right: var(--qas-spacing-md);
524
- }
525
-
526
- tr {
527
- position: relative;
528
510
 
529
- &::before {
530
- background-color: transparent;
531
- bottom: 0;
532
- content: '';
511
+ &:first-child::before {
533
512
  left: calc(var(--qas-spacing-md) * -1);
534
- pointer-events: none;
535
- position: absolute;
513
+ }
514
+
515
+ &:last-child::before {
536
516
  right: calc(var(--qas-spacing-md) * -1);
537
- top: 0;
538
- transition: background-color var(--qas-generic-transition);
539
517
  }
540
518
  }
541
519
 
542
- tbody tr {
543
- &::before {
544
- display: block;
545
- }
520
+ tr:hover td::before {
521
+ background-color: var(--qas-background-color);
522
+ }
546
523
 
547
- &:hover::before {
548
- background-color: var(--qas-background-color);
549
- }
524
+ &__middle {
525
+ padding-left: var(--qas-spacing-md);
526
+ padding-right: var(--qas-spacing-md);
527
+ }
550
528
 
529
+ tbody tr {
551
530
  /*
552
531
  A regra só é aplicada se nenhum elemento filho com o atributo "data-table-ignore-tr-hover"
553
532
  estiver também em estado de hover, impedindo que estilos conflitantes sejam aplicados.
@@ -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
  })
@@ -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