@bildvitta/quasar-ui-asteroid 3.20.0-beta.2 → 3.20.0-beta.21

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 (102) hide show
  1. package/package.json +3 -3
  2. package/src/asteroid.js +8 -1
  3. package/src/components/actions/QasActions.vue +12 -2
  4. package/src/components/actions-menu/QasActionsMenu.vue +18 -5
  5. package/src/components/alert/QasAlert.vue +89 -64
  6. package/src/components/app-user/QasAppUser.vue +2 -1
  7. package/src/components/board-generator/QasBoardGenerator.vue +883 -162
  8. package/src/components/board-generator/QasBoardGenerator.yml +83 -2
  9. package/src/components/board-generator/private/PvBoardGeneratorCardsContainer.vue +25 -0
  10. package/src/components/box/QasBox.vue +16 -3
  11. package/src/components/box/QasBox.yml +10 -0
  12. package/src/components/btn/QasBtn.vue +27 -5
  13. package/src/components/btn/QasBtn.yml +10 -1
  14. package/src/components/btn-dropdown/QasBtnDropdown.vue +13 -1
  15. package/src/components/card/QasCard.vue +97 -25
  16. package/src/components/card/QasCard.yml +10 -0
  17. package/src/components/card-image/QasCardImage.vue +10 -1
  18. package/src/components/card-image/QasCardImage.yml +5 -0
  19. package/src/components/chart-view/QasChartView.vue +4 -3
  20. package/src/components/chart-view/QasChartView.yml +5 -0
  21. package/src/components/checkbox/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  22. package/src/components/copy/QasCopy.vue +6 -1
  23. package/src/components/copy/QasCopy.yml +5 -0
  24. package/src/components/date-time-input/QasDateTimeInput.vue +30 -6
  25. package/src/components/dialog/QasDialog.vue +308 -91
  26. package/src/components/dialog/QasDialog.yml +51 -23
  27. package/src/components/dialog/composables/use-cancel.js +1 -1
  28. package/src/components/dialog/composables/use-dynamic-components.js +2 -2
  29. package/src/components/dialog/composables/use-ok.js +1 -0
  30. package/src/components/dialog-router/QasDialogRouter.vue +1 -1
  31. package/src/components/drawer/QasDrawer.vue +76 -26
  32. package/src/components/drawer/QasDrawer.yml +10 -0
  33. package/src/components/expansion-item/QasExpansionItem.yml +5 -0
  34. package/src/components/filters/QasFilters.vue +2 -1
  35. package/src/components/filters/private/PvFiltersActions.vue +79 -13
  36. package/src/components/form-generator/QasFormGenerator.vue +8 -1
  37. package/src/components/form-generator/QasFormGenerator.yml +10 -0
  38. package/src/components/form-view/QasFormView.vue +20 -11
  39. package/src/components/form-view/QasFormView.yml +6 -0
  40. package/src/components/gallery/composables/use-delete.js +2 -3
  41. package/src/components/gallery/private/PvGalleryCarouselDialog.vue +8 -7
  42. package/src/components/grid-item/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  43. package/src/components/header/QasHeader.vue +66 -11
  44. package/src/components/header/QasHeader.yml +16 -1
  45. package/src/components/infinite-scroll/QasInfiniteScroll.vue +1 -1
  46. package/src/components/label/QasLabel.vue +3 -1
  47. package/src/components/layout/QasLayout.vue +16 -1
  48. package/src/components/layout/private/PvLayoutNotificationsDrawer.vue +2 -1
  49. package/src/components/layout/private/PvLayoutOverlayDrawer.vue +4 -2
  50. package/src/components/lazy-loading-components/QasLazyLoadingComponents.vue +262 -0
  51. package/src/components/lazy-loading-components/QasLazyLoadingComponents.yml +49 -0
  52. package/src/components/list-view/QasListView.vue +12 -4
  53. package/src/components/list-view/QasListView.yml +12 -0
  54. package/src/components/page-header/QasPageHeader.vue +49 -3
  55. package/src/components/page-header/QasPageHeader.yml +5 -0
  56. package/src/components/router-link/QasRouterLink.vue +72 -0
  57. package/src/components/router-link/QasRouterLink.yml +24 -0
  58. package/src/components/search-box/QasSearchBox.vue +1 -1
  59. package/src/components/select/QasSelect.vue +8 -1
  60. package/src/components/select-list-dialog/QasSelectListDialog.vue +40 -20
  61. package/src/components/select-list-dialog/QasSelectListDialog.yml +14 -2
  62. package/src/components/signature-uploader/QasSignatureUploader.vue +5 -18
  63. package/src/components/single-view/QasSingleView.vue +2 -2
  64. package/src/components/skeleton/QasSkeleton.vue +139 -0
  65. package/src/components/skeleton/QasSkeleton.yml +48 -0
  66. package/src/components/sortable/QasSortable.vue +1 -1
  67. package/src/components/stepper/QasStepper.vue +24 -2
  68. package/src/components/table-generator/QasTableGenerator.vue +186 -35
  69. package/src/components/table-generator/QasTableGenerator.yml +6 -1
  70. package/src/components/tabs-generator/QasTabsGenerator.vue +14 -3
  71. package/src/components/tabs-generator/QasTabsGenerator.yml +5 -1
  72. package/src/components/text-truncate/QasTextTruncate.vue +61 -12
  73. package/src/components/text-truncate/QasTextTruncate.yml +5 -0
  74. package/src/components/toggle-visibility/QasToggleVisibility.vue +2 -1
  75. package/src/components/tooltip/QasTooltip.vue +6 -1
  76. package/src/components/tree-generator/QasTreeGenerator.vue +4 -6
  77. package/src/components/uploader/QasUploader.vue +12 -2
  78. package/src/composables/private/use-view.js +1 -1
  79. package/src/composables/use-overlay-navigation.js +116 -10
  80. package/src/composables/use-screen.js +17 -1
  81. package/src/css/components/button.scss +82 -3
  82. package/src/css/components/item.scss +6 -0
  83. package/src/css/utils/background.scss +5 -0
  84. package/src/css/utils/border.scss +6 -0
  85. package/src/css/utils/container.scss +4 -3
  86. package/src/css/utils/text.scss +9 -0
  87. package/src/helpers/copy-to-clipboard.js +2 -1
  88. package/src/helpers/filters.js +1 -1
  89. package/src/helpers/promise-handler.js +2 -1
  90. package/src/helpers/set-scroll-gradient.js +31 -8
  91. package/src/helpers/set-scroll-on-grab.js +10 -3
  92. package/src/index.scss +1 -0
  93. package/src/mixins/search-filter.js +7 -1
  94. package/src/plugins/delete/Delete.js +7 -9
  95. package/src/plugins/delete/Delete.yml +1 -1
  96. package/src/plugins/dialog/Dialog.yml +1 -1
  97. package/src/plugins/notify-error/NotifyError.yml +1 -1
  98. package/src/plugins/notify-success/NotifySuccess.yml +1 -1
  99. package/src/plugins/screen/Screen.js +17 -1
  100. package/src/plugins/screen/Screen.yml +5 -1
  101. package/src/vue-plugin.js +5 -7
  102. package/src/plugins/index.js +0 -5
@@ -0,0 +1,262 @@
1
+ <template>
2
+ <!--
3
+ Itera sobre todos os componentes extraídos do slot
4
+ Cada item pode estar em dois estados:
5
+ 1. Visível: Renderiza o componente real
6
+ 2. Não visível: Renderiza placeholder vazio (reserva espaço)
7
+ -->
8
+ <template
9
+ v-for="(item, index) in items"
10
+ :key="index"
11
+ >
12
+ <transition enter-active-class="animated fadeIn slow">
13
+ <!-- Componente real - renderizado quando visível -->
14
+ <component
15
+ :is="item"
16
+ v-if="visibleItems.has(index)"
17
+ />
18
+
19
+ <!-- Placeholder - div vazia que reserva espaço no layout -->
20
+ <div
21
+ v-else
22
+ :ref="element => setPlaceholderRef(element, index)"
23
+ />
24
+ </transition>
25
+ </template>
26
+ </template>
27
+
28
+ <script setup>
29
+ import { ref, onMounted, onBeforeUnmount, useSlots, nextTick, watch } from 'vue'
30
+
31
+ defineOptions({ name: 'QasLazyLoadingComponents' })
32
+
33
+ const props = defineProps({
34
+ threshold: {
35
+ type: Number,
36
+ default: 0.1 // 10% visível
37
+ },
38
+
39
+ rootMargin: {
40
+ type: String,
41
+ default: '0px'
42
+ },
43
+
44
+ direction: {
45
+ type: String,
46
+ default: 'vertical',
47
+ validator: value => ['vertical', 'horizontal'].includes(value)
48
+ },
49
+
50
+ placeholderHeight: {
51
+ type: String,
52
+ default: '500px'
53
+ },
54
+
55
+ placeholderWidth: {
56
+ type: String,
57
+ default: '300px'
58
+ }
59
+ })
60
+
61
+ // models
62
+ const visibleItemsModel = defineModel('visibleItems', { type: Array, default: () => [] })
63
+
64
+ // refs
65
+ /**
66
+ * Lista de VNodes extraídos do slot default
67
+ * Cada item é um VNode que representa um componente filho
68
+ *
69
+ * Exemplo:
70
+ * <qas-lazy-loading-components>
71
+ * <ComponenteA /> <- items[0]
72
+ * <ComponenteB /> <- items[1]
73
+ * </qas-lazy-loading-components>
74
+ */
75
+ const items = ref([])
76
+
77
+ /**
78
+ * Set de índices dos componentes que já foram renderizados
79
+ * Quando um placeholder entra no viewport, seu índice é adicionado aqui
80
+ * @type {Ref<Set<number>>}
81
+ */
82
+ const visibleItems = ref(new Set())
83
+
84
+ // composables
85
+ const slots = useSlots()
86
+
87
+ // consts
88
+ /**
89
+ * Map que armazena referências aos elementos placeholder
90
+ * Chave: índice do componente
91
+ * Valor: elemento DOM do placeholder
92
+ * @type {Map<number, HTMLElement>}
93
+ */
94
+ const placeholderRefs = new Map()
95
+
96
+ /**
97
+ * Instância do IntersectionObserver
98
+ * Responsável por detectar quando placeholders entram no viewport
99
+ * @type {IntersectionObserver | null}
100
+ */
101
+ let observer = null
102
+
103
+ // lifecycle hooks
104
+ onMounted(handleObserver)
105
+
106
+ // Observa mudanças no slot para atualizar items
107
+ watch(
108
+ () => slots.default?.(),
109
+ handleObserver,
110
+ { flush: 'post' }
111
+ )
112
+
113
+ onBeforeUnmount(() => {
114
+ if (!observer) return
115
+
116
+ // Limpa o observer para evitar memory leaks
117
+ observer.disconnect()
118
+ })
119
+
120
+ // functions
121
+ /**
122
+ * Cria e configura o IntersectionObserver
123
+ * Observa todos os placeholders e renderiza componentes quando visíveis
124
+ *
125
+ * Fluxo:
126
+ * 1. Cria observer com threshold e rootMargin configurados
127
+ * 2. Quando placeholder entra no viewport:
128
+ * - Encontra o índice correspondente no Map
129
+ * - Adiciona índice ao visibleItems (renderiza componente)
130
+ * - Para de observar esse elemento
131
+ * 3. Observa todos os placeholders armazenados no Map
132
+ */
133
+ function createObserver () {
134
+ observer = new IntersectionObserver(
135
+ entries => {
136
+ entries.forEach(entry => {
137
+ if (!entry.isIntersecting) return
138
+
139
+ // Converte Map em Array para facilitar busca
140
+ const list = Array.from(placeholderRefs.entries())
141
+
142
+ // Encontra o índice do placeholder no Map
143
+ const index = list.findIndex(([_, element]) => element === entry.target)
144
+
145
+ // Verifica se encontrou o índice (index !== -1)
146
+ if (!~index) return
147
+
148
+ // Adiciona ao Set para renderizar o componente real
149
+ visibleItems.value.add(index)
150
+
151
+ // Para de observar - componente já foi renderizado
152
+ observer.unobserve(entry.target)
153
+
154
+ // Emite os índices atualmente visíveis
155
+ visibleItemsModel.value = Array.from(visibleItems.value)
156
+ })
157
+ },
158
+ {
159
+ threshold: props.threshold,
160
+ rootMargin: props.rootMargin
161
+ }
162
+ )
163
+
164
+ // Observa todos os placeholders do Map
165
+ placeholderRefs.forEach(element => {
166
+ observer.observe(element)
167
+ })
168
+ }
169
+
170
+ function handleObserver () {
171
+ setItems()
172
+
173
+ // Reconstrói o observer com os novos items
174
+ if (observer) {
175
+ observer.disconnect()
176
+ }
177
+
178
+ nextTick(() => requestAnimationFrame(createObserver))
179
+ }
180
+
181
+ /**
182
+ * Extrai e achata os VNodes do slot default
183
+ * Suporta múltiplos cenários:
184
+ * - Componentes individuais: <ComponenteA /> <ComponenteB />
185
+ * - v-for: <Component v-for="..." />
186
+ * - template v-for: <template v-for="..."><Component /></template>
187
+ *
188
+ * @param {Array} vnodes - Array de VNodes
189
+ * @returns {Array} Array de VNodes achatado
190
+ */
191
+ function flattenVNodes (vnodes) {
192
+ const flattened = []
193
+
194
+ vnodes.forEach(vnode => {
195
+ // Ignora VNodes inválidos (null, undefined, primitivos)
196
+ if (!vnode || typeof vnode !== 'object') return
197
+
198
+ /**
199
+ * Se for um Fragment (v-for, template, v-if, etc), achata recursivamente os children
200
+ * Fragments são wrappers que o Vue cria para agrupar múltiplos elementos
201
+ */
202
+ const isFragment = typeof vnode.type === 'symbol' && Array.isArray(vnode.children)
203
+
204
+ if (isFragment) {
205
+ flattened.push(...flattenVNodes(vnode.children))
206
+ } else {
207
+ // VNode normal (componente ou elemento HTML)
208
+ flattened.push(vnode)
209
+ }
210
+ })
211
+
212
+ return flattened
213
+ }
214
+
215
+ /**
216
+ * Extrai os VNodes do slot default e armazena em items
217
+ * Achata automaticamente fragments e v-for
218
+ *
219
+ * Exemplo de uso:
220
+ * <qas-lazy-loading-components>
221
+ * <ComponenteA /> <- vnode 0
222
+ * <ComponenteB /> <- vnode 1
223
+ * <Component v-for="item in 10" /> <- vnode 2-11
224
+ * </qas-lazy-loading-components>
225
+ */
226
+ function setItems () {
227
+ const slotContent = slots.default?.() || []
228
+ const flattenedItems = flattenVNodes(slotContent)
229
+
230
+ items.value = flattenedItems
231
+ }
232
+
233
+ /**
234
+ * Armazena referência do elemento placeholder no Map
235
+ * Chamado automaticamente pelo Vue através do :ref no template
236
+ *
237
+ * @param {HTMLElement | null} element - Elemento DOM do placeholder
238
+ * @param {number} index - Índice do componente
239
+ */
240
+ function setPlaceholderRef (element, index) {
241
+ if (!element) return
242
+
243
+ placeholderRefs.set(index, element)
244
+
245
+ const itemProps = items.value[index].props
246
+
247
+ if (props.direction === 'horizontal') {
248
+ /**
249
+ * Define a largura do placeholder com base no atributo "data-placeholder-width" caso queira customizar
250
+ * para o elemento específico, e não o definido na prop placeholderWidth.
251
+ */
252
+ element.style.width = itemProps?.['data-placeholder-width'] || props.placeholderWidth
253
+ element.style.flexShrink = '0'
254
+ } else {
255
+ /**
256
+ * Define a altura do placeholder com base no atributo "data-placeholder-height" caso queira customizar
257
+ * para o elemento específico, e não o definido na prop placeholderHeight.
258
+ */
259
+ element.style.height = itemProps?.['data-placeholder-height'] || props.placeholderHeight
260
+ }
261
+ }
262
+ </script>
@@ -0,0 +1,49 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Componente para carregar elementos do slot somente quando ficam visíveis na viewport, otimizando performance da página.
5
+
6
+ props:
7
+ direction:
8
+ desc: Direção do scroll e do lazy loading. Use `vertical` (padrão) para listas de rolagem vertical e `horizontal` para listas de rolagem horizontal.
9
+ type: String
10
+ default: vertical
11
+ values: vertical | horizontal
12
+
13
+ placeholder-height:
14
+ desc: Altura do placeholder exibido enquanto o elemento não está visível (usado quando `direction` é `vertical`).
15
+ default: '500px'
16
+ type: String
17
+
18
+ placeholder-width:
19
+ desc: Largura do placeholder exibido enquanto o elemento não está visível (usado quando `direction` é `horizontal`).
20
+ default: '300px'
21
+ type: String
22
+
23
+ root-margin:
24
+ desc: Margem extra ao redor do viewport para pré-carregamento
25
+ default: '0px'
26
+ type: String
27
+
28
+ threshold:
29
+ desc: Threshold do IntersectionObserver (0 = aparece um pouco, 1 = totalmente visível)
30
+ default: 0.1
31
+ type: Number
32
+
33
+ visible-items:
34
+ desc: Expõe os índices dos itens atualmente visíveis na viewport. Útil para otimizar renders e priorizar carregamento de dados.
35
+ type: Array
36
+ default: []
37
+ model: true
38
+
39
+ slots:
40
+ default:
41
+ desc: Elementos/componentes que serão carregados conforme ficam visíveis, onde cada elemento irmão dentro do slot será tratado individualmente para o lazy loading.
42
+
43
+ events:
44
+ '@update:visible-items -> function(value)':
45
+ desc: Dispara toda vez que os índices dos itens visíveis são atualizados, também utilizado para v-model:visible-items.
46
+ params:
47
+ value:
48
+ desc: Array de índices dos itens atualmente visíveis na viewport.
49
+ type: Array
@@ -13,7 +13,7 @@
13
13
  <div v-if="showResults">
14
14
  <slot />
15
15
 
16
- <q-inner-loading :showing="mx_isFetching">
16
+ <q-inner-loading :showing="mx_isFetching && useLoading">
17
17
  <q-spinner color="grey" size="3em" />
18
18
  </q-inner-loading>
19
19
  </div>
@@ -108,6 +108,11 @@ export default {
108
108
  type: Boolean
109
109
  },
110
110
 
111
+ useLoading: {
112
+ type: Boolean,
113
+ default: true
114
+ },
115
+
111
116
  usePagination: {
112
117
  default: true,
113
118
  type: Boolean
@@ -129,6 +134,7 @@ export default {
129
134
  },
130
135
 
131
136
  emits: [
137
+ 'fetch-start',
132
138
  'fetch-success',
133
139
  'fetch-error',
134
140
  'update:errors',
@@ -158,7 +164,7 @@ export default {
158
164
  * automaticamente.
159
165
  */
160
166
  hasDeleteEventListener () {
161
- return this.useAutoHandleOnDelete || this.useAutoRefetchOnDelete || (!this.useStore && this.entity)
167
+ return this.useAutoHandleOnDelete || this.useAutoRefetchOnDelete
162
168
  },
163
169
 
164
170
  hasHeaderSlot () {
@@ -189,7 +195,7 @@ export default {
189
195
  },
190
196
 
191
197
  showResults () {
192
- return this.hasResults || this.useResultsAreaOnly
198
+ return this.hasResults || this.useResultsAreaOnly || !this.useLoading
193
199
  },
194
200
 
195
201
  paginationClasses () {
@@ -251,6 +257,8 @@ export default {
251
257
  ...externalPayload
252
258
  }
253
259
 
260
+ this.$emit('fetch-start', payload)
261
+
254
262
  const response = await this.handleFetchList(payload)
255
263
 
256
264
  const { errors, fields, metadata, results, count } = response.data
@@ -374,7 +382,7 @@ export default {
374
382
  },
375
383
 
376
384
  onDeleteResult (event) {
377
- if (this.useAutoRefetchOnDelete || !this.useStore) {
385
+ if (this.useAutoRefetchOnDelete) {
378
386
  this.mx_fetchHandler({ ...this.mx_context, url: this.url }, this.fetchList)
379
387
  return
380
388
  }
@@ -86,6 +86,11 @@ props:
86
86
  default: true
87
87
  type: Boolean
88
88
 
89
+ use-loading:
90
+ desc: Controla se o componente vai exibir um loading quando estiver fazendo fetch dos dados.
91
+ default: true
92
+ type: Boolean
93
+
89
94
  use-pagination:
90
95
  desc: Controla se vai ter ou não paginação.
91
96
  default: true
@@ -125,6 +130,13 @@ slots:
125
130
  desc: 'Slot para acessar o header.'
126
131
 
127
132
  events:
133
+ '@fetch-start -> function(value)':
134
+ desc: Dispara logo antes da action "fetchList".
135
+ params:
136
+ value:
137
+ desc: Retorna o payload enviado para o fetchList, contendo paginação, filtros, search, etc.
138
+ type: Object
139
+
128
140
  '@fetch-success -> function(value)':
129
141
  desc: Dispara quando a action "fetchList" é executada com sucesso.
130
142
  params:
@@ -3,13 +3,23 @@
3
3
  <q-toolbar class="justify-between q-mb-md q-px-none qas-page-header">
4
4
  <div class="ellipsis">
5
5
  <q-toolbar-title v-if="props.title" class="text-h3">
6
- {{ props.title }}
6
+ <qas-skeleton v-if="props.skeleton" max-width="300px" type="text" use-contrast />
7
+
8
+ <template v-else>
9
+ {{ props.title }}
10
+ </template>
7
11
  </q-toolbar-title>
8
12
 
9
13
  <q-breadcrumbs v-if="hasBreadcrumbs" class="text-caption" gutter="xs" separator-color="grey-8">
10
14
  <q-breadcrumbs-el v-if="props.useHomeIcon" class="qas-page-header__breadcrumbs-el text-grey-8" icon="sym_r_home" :to="homeRoute" />
11
15
 
12
- <q-breadcrumbs-el v-for="(item, index) in normalizedBreadcrumbs" :key="index" class="ellipsis inline-block qas-page-header__breadcrumbs-el" :label="item.label" :to="item.route" />
16
+ <q-breadcrumbs-el v-for="(item, index) in normalizedBreadcrumbs" :key="index" class="ellipsis inline-block qas-page-header__breadcrumbs-el" tag="div" :to="item.route">
17
+ <qas-skeleton v-if="props.skeleton" v-bind="getBreadcrumbSkeletonProps(item)" />
18
+
19
+ <template v-else>
20
+ {{ item.label }}
21
+ </template>
22
+ </q-breadcrumbs-el>
13
23
  </q-breadcrumbs>
14
24
  </div>
15
25
 
@@ -25,6 +35,7 @@
25
35
  </template>
26
36
 
27
37
  <script setup>
38
+ import QasSkeleton from '../skeleton/QasSkeleton.vue'
28
39
  import QasHeader from '../header/QasHeader.vue'
29
40
 
30
41
  import { useOverlayNavigation } from '../../composables'
@@ -52,6 +63,10 @@ const props = defineProps({
52
63
  type: [Object, String]
53
64
  },
54
65
 
66
+ skeleton: {
67
+ type: Boolean
68
+ },
69
+
55
70
  title: {
56
71
  default: '',
57
72
  type: String
@@ -77,6 +92,7 @@ useMeta(() => ({ title: props.title }))
77
92
 
78
93
  // computed
79
94
  const hasBreadcrumbs = computed(() => props.useBreadcrumbs && !isOverlay)
95
+
80
96
  const transformedBreadcrumbs = computed(() => {
81
97
  const list = [...castArray(props.breadcrumbs || props.title)]
82
98
 
@@ -95,7 +111,7 @@ const transformedBreadcrumbs = computed(() => {
95
111
  })
96
112
  })
97
113
 
98
- const normalizedBreadcrumbs = computed(() => {
114
+ const truncatedBreadcrumbs = computed(() => {
99
115
  const breadcrumbsSize = transformedBreadcrumbs.value.length
100
116
 
101
117
  if (breadcrumbsSize < 5) return transformedBreadcrumbs.value
@@ -116,9 +132,39 @@ const normalizedBreadcrumbs = computed(() => {
116
132
  ]
117
133
  })
118
134
 
135
+ /**
136
+ * Caso a prop skeleton esteja ativa, retorna um array com 3 elementos vazios para renderizar
137
+ * os esqueletos dos breadcrumbs. Caso contrário, retorna os breadcrumbs truncados.
138
+ */
139
+ const normalizedBreadcrumbs = computed(() => {
140
+ if (props.skeleton) {
141
+ return Array.from({ length: 3 }).map(() => ({ label: '', route: null }))
142
+ }
143
+
144
+ return truncatedBreadcrumbs.value
145
+ })
146
+
119
147
  const hasHeader = computed(() => !!Object.keys(props.headerProps).length)
120
148
 
121
149
  const homeRoute = computed(() => router.hasRoute('Root') ? { name: 'Root' } : '/')
150
+
151
+ // functions
152
+ /**
153
+ * Retorna propriedades para o componente QasSkeleton usado nos breadcrumbs
154
+ */
155
+ function getBreadcrumbSkeletonProps () {
156
+ const min = 60
157
+ const max = 160
158
+
159
+ // Gera uma largura aleatória entre min e max entre 60 e 160 pixels
160
+ const width = Math.floor(Math.random() * (max - min + 1)) + min
161
+
162
+ return {
163
+ type: 'text',
164
+ useContrast: true,
165
+ width: `${width}px`
166
+ }
167
+ }
122
168
  </script>
123
169
 
124
170
  <style lang="scss">
@@ -24,6 +24,11 @@ props:
24
24
  desc: Rota raiz do breadcrumbs.
25
25
  type: [Object, String]
26
26
 
27
+ skeleton:
28
+ desc: Exibe um esqueleto de carregamento no lugar do conteúdo.
29
+ default: false
30
+ type: Boolean
31
+
27
32
  use-breadcrumbs:
28
33
  desc: Habilita ou não o breadcrumbs.
29
34
  default: true
@@ -0,0 +1,72 @@
1
+ <!--
2
+ Tratamento feito para ter o comportamento de ao abrir em uma nova guia, direto na página, abre sem overlay.
3
+ Caso contrário, ao clicar normalmente, abrirá no overlay.
4
+ -->
5
+ <template>
6
+ <router-link
7
+ class="qas-router-link text-no-decoration"
8
+ v-bind="routerLinkProps"
9
+ >
10
+ <slot>
11
+ {{ props.label }}
12
+ </slot>
13
+ </router-link>
14
+ </template>
15
+
16
+ <script setup>
17
+ import { computed } from 'vue'
18
+ import { useRouter } from 'vue-router'
19
+ import { useOverlayNavigation } from 'asteroid'
20
+
21
+ defineOptions({ name: 'QasRouterLink' })
22
+
23
+ const props = defineProps({
24
+ label: {
25
+ type: String,
26
+ default: ''
27
+ },
28
+
29
+ route: {
30
+ type: Object,
31
+ required: true
32
+ },
33
+
34
+ useOverlayRoute: {
35
+ type: Boolean
36
+ }
37
+ })
38
+
39
+ // composables
40
+ const router = useRouter()
41
+ const { getOverlayRoute } = useOverlayNavigation()
42
+
43
+ // computeds
44
+ const routerLinkProps = computed(() => {
45
+ return {
46
+ to: props.route,
47
+
48
+ ...(props.useOverlayRoute && {
49
+ onClick: event => {
50
+ /**
51
+ * @click.prevent pra evitar com que o router-link já trate o clique e tente navegar para a rota
52
+ * normalmente, o que não é o desejado quando queremos abrir um overlay
53
+ */
54
+ event.preventDefault()
55
+
56
+ router.push(getOverlayRoute(props.route))
57
+ }
58
+ })
59
+ }
60
+ })
61
+ </script>
62
+
63
+ <style lang="scss">
64
+ .qas-router-link {
65
+ transition: color var(--qas-generic-transition);
66
+ color: inherit;
67
+
68
+ &:hover {
69
+ color: $primary;
70
+ }
71
+ }
72
+ </style>
@@ -0,0 +1,24 @@
1
+ type: component
2
+
3
+ meta:
4
+ desc: Componente de link baseado no router-link. Ao passar a prop use-overlay-route, o clique normal abre a rota no overlay. Ao abrir em uma nova guia, navega diretamente para a página, sem overlay.
5
+
6
+ props:
7
+ label:
8
+ desc: Texto exibido como conteúdo padrão do link, utilizado quando o slot default não é fornecido.
9
+ default: ''
10
+ type: String
11
+
12
+ route:
13
+ desc: Rota de destino do link, repassada diretamente ao `router-link`.
14
+ required: true
15
+ type: Object
16
+
17
+ use-overlay-route:
18
+ desc: Quando verdadeiro, o clique no link abre a rota no overlay em vez de navegar normalmente. Ao abrir em uma nova guia, o comportamento padrão do `router-link` é mantido.
19
+ default: false
20
+ type: Boolean
21
+
22
+ slots:
23
+ default:
24
+ desc: Conteúdo do link. Quando não fornecido, exibe o valor da prop `label`.
@@ -138,7 +138,7 @@ export default {
138
138
  },
139
139
 
140
140
  hasNoOptionsOnFirstFetch () {
141
- return this.mx_fetchCount === 1 && !this.mx_hasFilteredOptions
141
+ return this.mx_fetchCount === 1 && !this.mx_hasFilteredOptions && !this.mx_hasNextPage
142
142
  },
143
143
 
144
144
  containerHeight () {
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <q-select v-model="model" v-bind="attributes" class="qas-select" :class="componentClasses" no-error-icon>
2
+ <q-select ref="select" v-model="model" v-bind="attributes" class="qas-select" :class="componentClasses" no-error-icon>
3
3
  <template v-if="hasIcon" #prepend>
4
4
  <q-icon :name="defaultIcon" />
5
5
  </template>
@@ -409,6 +409,13 @@ export default {
409
409
 
410
410
  onPopupHide () {
411
411
  this.isPopupContentOpen = false
412
+
413
+ /**
414
+ * necessário limpar o campo de input de pesquisa ao fechar o popup, para evitar que o valor da busca permaneça
415
+ * no campo quando abrir novamente e também o texto não sobreponha os itens selecionados ao apertar a tecla tab.
416
+ */
417
+ if (this.isSearchable) this.$refs.select.updateInputValue('', true)
418
+
412
419
  this.$emit('popup-hide')
413
420
  },
414
421