@bildvitta/quasar-ui-asteroid 3.20.0-beta.14-alpha.4 → 3.20.0-beta.14

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 (27) hide show
  1. package/package.json +1 -1
  2. package/src/components/actions-menu/QasActionsMenu.vue +4 -4
  3. package/src/components/board-generator/QasBoardGenerator.vue +165 -99
  4. package/src/components/board-generator/QasBoardGenerator.yml +56 -2
  5. package/src/components/board-generator/private/PvBoardGeneratorCardsContainer.vue +0 -1
  6. package/src/components/box/QasBox.yml +5 -0
  7. package/src/components/card/QasCard.vue +1 -0
  8. package/src/components/date-time-input/QasDateTimeInput.vue +30 -6
  9. package/src/components/dialog/QasDialog.vue +1 -3
  10. package/src/components/dialog/QasDialog.yml +8 -0
  11. package/src/components/expansion-item/QasExpansionItem.yml +5 -0
  12. package/src/components/form-generator/QasFormGenerator.yml +5 -0
  13. package/src/components/header/QasHeader.yml +5 -0
  14. package/src/components/lazy-loading-components/QasLazyLoadingComponents.vue +0 -7
  15. package/src/components/lazy-loading-components/QasLazyLoadingComponents.yml +34 -9
  16. package/src/components/select-list-dialog/QasSelectListDialog.vue +1 -1
  17. package/src/components/stepper/QasStepper.vue +24 -2
  18. package/src/composables/use-screen.js +17 -1
  19. package/src/helpers/filters.js +1 -1
  20. package/src/helpers/set-scroll-gradient.js +5 -2
  21. package/src/mixins/search-filter.js +1 -1
  22. package/src/plugins/delete/Delete.yml +1 -1
  23. package/src/plugins/dialog/Dialog.yml +1 -1
  24. package/src/plugins/notify-error/NotifyError.yml +1 -1
  25. package/src/plugins/notify-success/NotifySuccess.yml +1 -1
  26. package/src/plugins/screen/Screen.js +17 -1
  27. package/src/plugins/screen/Screen.yml +5 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bildvitta/quasar-ui-asteroid",
3
3
  "description": "Asteroid",
4
- "version": "3.20.0-beta.14-alpha.4",
4
+ "version": "3.20.0-beta.14",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "./src/asteroid.js",
@@ -6,11 +6,11 @@
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
8
  <q-spinner v-if="item.loading" size="sm" />
9
- <q-icon v-else :class="getMagicAiClass(item)" :name="item.icon" />
9
+ <q-icon v-else :class="getMagicAiClasses(item)" :name="item.icon" />
10
10
  </q-item-section>
11
11
 
12
12
  <q-item-section>
13
- <q-item-label :class="getMagicAiClass(item)">
13
+ <q-item-label :class="getMagicAiClasses(item)">
14
14
  {{ item.label }}
15
15
  </q-item-label>
16
16
  </q-item-section>
@@ -286,8 +286,8 @@ function getItemProps (item) {
286
286
  }
287
287
  }
288
288
 
289
- function getMagicAiClass (item) {
290
- return { 'text-magic-ai': item.useMagicAiColor }
289
+ function getMagicAiClasses ({ useMagicAiColor }) {
290
+ return { 'text-magic-ai': useMagicAiColor }
291
291
  }
292
292
 
293
293
  function handleMenuModel (newValue, oldValue) {
@@ -3,8 +3,8 @@
3
3
  <qas-grabbable class="qas-board-generator" v-bind="grabbableProps">
4
4
  <div ref="columnsContainer" class="no-wrap q-gutter-md q-pb-xs q-px-lg row">
5
5
  <qas-lazy-loading-components :key="lazyLoadingKey" v-model:visible-items="visibleItems" direction="horizontal" placeholder-width="350px" :threshold="0">
6
- <qas-box v-for="(header, index) in normalizedHeaders" :key="index" :class="getColumnClass(header)" :style="containerStyle">
7
- <div class="ellipsis q-mb-md text-grey-10" v-bind="headerBoxProps">
6
+ <qas-box v-for="(header, index) in normalizedHeaders" :key="index" :class="getColumnClasses(header)" :style="containerStyle">
7
+ <div class="ellipsis q-mb-md text-grey-10">
8
8
  <qas-skeleton v-if="props.skeleton" type="text" use-contrast width="80%" />
9
9
 
10
10
  <slot v-else :fields="getFieldsByHeader(header)" :header="header" :index="index" name="header-column" />
@@ -22,24 +22,19 @@
22
22
  </div>
23
23
 
24
24
  <div class="text-center">
25
- <qas-btn class="q-mt-md" icon="sym_r_refresh" label="Tentar novamente" :loading="columnsLoading[getKeyByHeader(header)]" @click="fetchColumn(header, true)" />
25
+ <qas-btn class="q-mt-md" icon="sym_r_refresh" label="Tentar novamente" :loading="columnsLoading[getKeyByHeader(header)]" @click="handleFetchColumnClick(header)" />
26
26
  </div>
27
27
  </div>
28
28
 
29
29
  <template v-else>
30
30
  <qas-lazy-loading-components :threshold="0">
31
31
  <div v-for="(item) in getItemsByHeader(header)" :id="item[props.itemIdKey]" :key="item[props.itemIdKey]" class="qas-board-generator__item" :data-disable-drag="updatingPositionItemKeys.has(item[props.itemIdKey])">
32
- <!-- <slot v-if="!props.skeleton" :column-index="index" :fields="getFieldsByHeader(header)" :header="header" :item="item" name="column-item" /> -->
33
- <!-- <qas-card v-if="updatingPositionItemKey === item[props.itemIdKey]" v-bind="skeletonCards.at(0)" :key="item[props.itemIdKey]" class="q-mb-sm" :column-index="index">
34
- <template #default />
35
- </qas-card> -->
36
-
37
32
  <slot v-if="!props.skeleton" :column-index="index" :fields="getFieldsByHeader(header)" :header="header" :is-updating-position="updatingPositionItemKeys.has(item[props.itemIdKey])" :item="item" name="column-item" />
38
33
  </div>
39
34
  </qas-lazy-loading-components>
40
35
 
41
36
  <div class="full-width justify-center row">
42
- <qas-btn v-if="hasSeeMore(header)" icon="sym_r_add" :label="props.seeMoreButtonLabel" :loading="columnsLoading[getKeyByHeader(header)]" :use-label-on-small-screen="false" variant="tertiary" @click="fetchColumn(header, true)" />
37
+ <qas-btn v-if="hasSeeMore(header)" icon="sym_r_add" :label="props.seeMoreButtonLabel" :loading="columnsLoading[getKeyByHeader(header)]" :use-label-on-small-screen="false" variant="tertiary" @click="handleFetchColumnClick(header)" />
43
38
 
44
39
  <template v-if="hasSkeletonByHeader(header)">
45
40
  <div class="q-col-gutter-y-sm row">
@@ -117,11 +112,6 @@ const props = defineProps({
117
112
  default: () => ({})
118
113
  },
119
114
 
120
- headerBoxProps: {
121
- type: Object,
122
- default: () => ({})
123
- },
124
-
125
115
  columnIdKey: {
126
116
  type: String,
127
117
  required: true
@@ -182,13 +172,7 @@ const props = defineProps({
182
172
  },
183
173
 
184
174
  skeleton: {
185
- type: Boolean,
186
- default: true
187
- },
188
-
189
- useMarkRaw: {
190
- type: Boolean,
191
- default: true
175
+ type: Boolean
192
176
  },
193
177
 
194
178
  useDragAndDropX: {
@@ -247,8 +231,13 @@ const columnsLoading = ref({})
247
231
  const columnsFieldsModel = ref({})
248
232
  const showConfirmDialog = ref(false)
249
233
  const isLoadingUpdatePosition = ref(false)
250
- const isLoadingFromSeeMore = ref(false)
234
+ const hideSkeleton = ref(false)
251
235
  const columnsWithError = ref({})
236
+
237
+ /**
238
+ * Set de IDs dos itens com update de posição em andamento.
239
+ * Usamos Set para evitar duplicatas e ter O(1) no has().
240
+ */
252
241
  const updatingPositionItemKeys = ref(new Set())
253
242
 
254
243
  /**
@@ -309,7 +298,10 @@ let currentFetchColumnsSession = 0
309
298
 
310
299
  /**
311
300
  * Estado do smooth scroll horizontal.
312
- * Acumula o target e interpola suavemente via requestAnimationFrame.
301
+ * Acumula o target e interpola suavemente via requestAnimationFrame (rAF).
302
+ *
303
+ * rAF (requestAnimationFrame) sincroniza a animação com o refresh da tela (~60fps).
304
+ * Isso evita "saltos" de scroll, tornando o movimento suave e fluido.
313
305
  */
314
306
  const SMOOTH_SCROLL_LERP = 0.2
315
307
 
@@ -352,13 +344,15 @@ const grabbableProps = {
352
344
  * Gera cards de skeleton para exibir enquanto carrega os itens da coluna, o mesmo é usado para preencher a coluna
353
345
  * quando a prop "skeleton" for true, indicando que é para simular o carregamento.
354
346
  */
355
- const skeletonCards = Array.from({ length: 6 }).map(() => ({
356
- skeleton: true,
357
- title: '-',
358
- expansionProps: { label: '-' },
359
- actionsMenuProps: { list: {} },
360
- useSelection: true
361
- }))
347
+ const skeletonCards = Array.from({ length: 6 }).map(() => {
348
+ return {
349
+ skeleton: true,
350
+ title: '-',
351
+ expansionProps: { label: '-' },
352
+ actionsMenuProps: { list: {} },
353
+ useSelection: true
354
+ }
355
+ })
362
356
 
363
357
  // computeds
364
358
  const columnContainerElements = computed(() => {
@@ -378,38 +372,6 @@ const normalizedHeaders = computed(() => {
378
372
  return props.headers
379
373
  })
380
374
 
381
- // watchers
382
- watch(
383
- () => normalizedHeaders.value,
384
- value => {
385
- if (isLoadingUpdatePosition.value || !value?.length) return
386
-
387
- fetchColumnsValues()
388
- }
389
- )
390
-
391
- watch(() => columnContainerElements.value, () => {
392
- setColumnHeightContainer()
393
- handleElementsList()
394
- })
395
-
396
- // hooks
397
- onMounted(() => {
398
- if (normalizedHeaders.value.length) {
399
- fetchColumnsValues()
400
- }
401
-
402
- window.addEventListener('resize', setColumnHeightContainer)
403
- })
404
-
405
- onBeforeUnmount(() => {
406
- abortAllColumnRequests()
407
- destroySortable()
408
-
409
- window.removeEventListener('resize', setColumnHeightContainer)
410
- })
411
-
412
- // Computeds
413
375
  const columnsResultsModel = computed({
414
376
  get () {
415
377
  return props.results
@@ -422,8 +384,6 @@ const columnsResultsModel = computed({
422
384
 
423
385
  const isDragging = computed(() => draggingCount.value > 0)
424
386
 
425
- provide('isDragging', isDragging)
426
-
427
387
  const hasColumnsLength = computed(() => !!Object.keys(columnsResultsModel.value).length)
428
388
 
429
389
  const containerStyle = computed(() => `width: ${props.columnWidth};`)
@@ -449,12 +409,54 @@ const defaultConfirmDialogProps = computed(() => {
449
409
  }
450
410
  })
451
411
 
412
+ // provide
413
+ provide('isDragging', isDragging)
414
+
415
+ // watchers
416
+ watch(
417
+ () => normalizedHeaders.value,
418
+ value => {
419
+ if (isLoadingUpdatePosition.value || !value?.length) return
420
+
421
+ fetchColumnsValues()
422
+ }
423
+ )
424
+
425
+ watch(() => columnContainerElements.value, () => {
426
+ setColumnHeightContainer()
427
+ handleElementsList()
428
+ })
429
+
430
+ // hooks
431
+ onMounted(() => {
432
+ if (normalizedHeaders.value.length) {
433
+ fetchColumnsValues()
434
+ }
435
+
436
+ window.addEventListener('resize', setColumnHeightContainer)
437
+ })
438
+
439
+ onBeforeUnmount(() => {
440
+ abortAllColumnRequests()
441
+ destroySortable()
442
+
443
+ window.removeEventListener('resize', setColumnHeightContainer)
444
+ })
445
+
452
446
  // functions
453
447
  /*
454
448
  * Setar o tamanho do container do board, onde deverá ser a altura passada via prop, ou o default será ocupar o maximo
455
449
  * de espaço que ele conseguir considerando a altura do container em relação ao topo.
456
450
  */
457
451
  function setColumnHeightContainer () {
452
+ if (props.height) {
453
+ columnContainerElements.value.forEach(columnElement => {
454
+ columnElement.style.height = props.height
455
+ })
456
+
457
+ return
458
+ }
459
+
458
460
  // Primeira etapa: calcula e aplica a altura inicial de cada coluna
459
461
  columnContainerElements.value.forEach(columnElement => {
460
462
  // Pega a posição atual da coluna em relação ao topo da viewport
@@ -514,7 +516,7 @@ async function fetchColumns () {
514
516
  const mySession = ++currentFetchColumnsSession
515
517
 
516
518
  // Mapeia os índices visíveis para os IDs de coluna correspondentes
517
- const visibleKeys = new Set(visibleItems.value.map(i => getKeyByHeader(normalizedHeaders.value[i])))
519
+ const visibleKeys = new Set(visibleItems.value.map(item => getKeyByHeader(normalizedHeaders.value[item])))
518
520
 
519
521
  const visibleHeaders = visibleKeys.size
520
522
  ? normalizedHeaders.value.filter(header => visibleKeys.has(getKeyByHeader(header)))
@@ -548,6 +550,7 @@ async function fetchColumns () {
548
550
  if (currentFetchColumnsSession !== mySession) return
549
551
 
550
552
  const allPromises = [...visibleColumns, ...hiddenColumns]
553
+ console.log('🚀 ~ fetchColumns ~ allPromises:', allPromises)
551
554
 
552
555
  const hasAllPromisesSucceeded = allPromises.every(promise => promise.status === 'fulfilled')
553
556
  const hasAllPromisesFailed = allPromises.length > 0 && allPromises.every(promise => promise.status === 'rejected')
@@ -568,18 +571,36 @@ async function fetchColumns () {
568
571
  }
569
572
 
570
573
  /*
571
- * Busca a coluna com base no header recebido.
574
+ * Wrapper para chamadas de fetchColumn originadas de eventos de clique do usuário.
575
+ * fetchColumn relança o erro para que Promise.allSettled em fetchColumns consiga
576
+ * detectar colunas com falha; aqui o erro já foi tratado internamente, portanto
577
+ * é suprimido para evitar o warning "Unhandled error during execution of component
578
+ * event handler" do Vue.
572
579
  */
573
- async function fetchColumn (header, fromSeeMore, setEr) {
580
+ function handleFetchColumnClick (header) {
581
+ fetchColumn(header, true)
582
+ }
583
+
584
+ /**
585
+ * Busca a coluna com base no header recebido.
586
+ *
587
+ * @param header - payload do header da coluna a ser buscada, exemplo: { date: '2024-02-12', ... }
588
+ * @param shouldHideSkeleton - flag para controlar se o skeleton de carregamento deve ser ocultado durante a busca
589
+ */
590
+ async function fetchColumn (header, shouldHideSkeleton) {
574
591
  const headerKey = getKeyByHeader(header)
575
592
 
576
- // Cancela qualquer request anterior para esta mesma coluna antes de iniciar a nova.
577
- // Garante que nunca haja duas requests paralelas para a mesma coluna.
593
+ /**
594
+ * Cancela qualquer request anterior para esta mesma coluna antes de iniciar a nova.
595
+ * Garante que nunca haja duas requests paralelas para a mesma coluna.
596
+ */
578
597
  abortColumnRequest(headerKey)
579
598
 
580
- // Incrementa a geração desta coluna para invalidar callbacks onLoading da request cancelada,
581
- // evitando que o onLoading(false) antigo sobrescreva o estado da nova request.
582
- // Usa ?? 0 pois ++undefined retorna NaN, e NaN === NaN é sempre false, travando o skeleton.
599
+ /**
600
+ * Incrementa a geração desta coluna para invalidar callbacks onLoading da request cancelada,
601
+ * evitando que o onLoading(false) antigo sobrescreva o estado da nova request.
602
+ * Usa ?? 0 pois ++undefined retorna NaN, e NaN === NaN é sempre false, travando o skeleton.
603
+ */
583
604
  columnFetchGenerations[headerKey] = (columnFetchGenerations[headerKey] ?? 0) + 1
584
605
  const generation = columnFetchGenerations[headerKey]
585
606
 
@@ -588,10 +609,10 @@ async function fetchColumn (header, fromSeeMore, setEr) {
588
609
 
589
610
  const { limit, offset } = columnsPagination.value[headerKey] || {}
590
611
 
591
- isLoadingFromSeeMore.value = fromSeeMore
612
+ hideSkeleton.value = shouldHideSkeleton
592
613
 
593
614
  const { data: response, error } = await promiseHandler(
594
- axios.get(`${props.columnUrl}/${headerKey}/${setEr ? 'setError' : ''}`, {
615
+ axios.get(`${props.columnUrl}/${headerKey}`, {
595
616
  signal: abortController.signal,
596
617
  params: {
597
618
  ...props.columnParams,
@@ -601,8 +622,10 @@ async function fetchColumn (header, fromSeeMore, setEr) {
601
622
  }),
602
623
  {
603
624
  onLoading: value => {
604
- // Só atualiza o loading se ainda for a request atual desta coluna,
605
- // evitando que o onLoading(false) de uma request cancelada sobrescreva o da nova.
625
+ /**
626
+ * atualiza o loading se ainda for a request atual desta coluna,
627
+ * evitando que o onLoading(false) de uma request cancelada sobrescreva o da nova.
628
+ */
606
629
  if (columnFetchGenerations[headerKey] === generation) {
607
630
  columnsLoading.value[headerKey] = value
608
631
  }
@@ -613,7 +636,7 @@ async function fetchColumn (header, fromSeeMore, setEr) {
613
636
 
614
637
  delete columnAbortControllers[headerKey]
615
638
 
616
- isLoadingFromSeeMore.value = false
639
+ hideSkeleton.value = false
617
640
 
618
641
  if (error) {
619
642
  // Request cancelada intencionalmente (novo fetchColumnsValues chamado): ignora silenciosamente.
@@ -647,7 +670,7 @@ async function fetchColumn (header, fromSeeMore, setEr) {
647
670
  * onde cada item do objeto é uma coluna no board. O mesmo vale para "columnsFieldsModel", "columnsLoading" e
648
671
  * "columnPagination", organizando os fields, loadings e o controle de paginação por chave identificadora do header.
649
672
  */
650
- columnsResultsModel.value[headerKey] = props.useMarkRaw ? markRaw(newColumnValues) : newColumnValues
673
+ columnsResultsModel.value[headerKey] = hasDragAndDrop ? newColumnValues : markRaw(newColumnValues)
651
674
 
652
675
  /*
653
676
  * Pode acontecer das options nos fields da segunda página serem diferentes da primeira página,
@@ -755,15 +778,19 @@ function setColumnsPagination () {
755
778
 
756
779
  columnsPagination.value[headerKey] = { limit: props.limitPerColumn, offset: 0 }
757
780
 
758
- // Inicia como true para exibir skeleton imediatamente, evitando flash de tela vazia
759
- // entre o reset e o início das novas requests.
781
+ /**
782
+ * Inicia como true para exibir skeleton imediatamente, evitando flash de tela vazia
783
+ * entre o reset e o início das novas requests.
784
+ */
760
785
  columnsLoading.value[headerKey] = true
761
786
  })
762
787
  }
763
788
 
764
789
  function fetchColumnsValues () {
765
- // Cancela todas as requests em andamento antes de iniciar novas,
766
- // evitando resposta de requests antigas sobrescrevendo dados da nova sessão.
790
+ /**
791
+ * Cancela todas as requests em andamento antes de iniciar novas, evitando resposta de requests
792
+ * antigas sobrescrevendo dados da nova sessão.
793
+ */
767
794
  abortAllColumnRequests()
768
795
 
769
796
  lazyLoadingKey.value++
@@ -777,10 +804,10 @@ function fetchColumnsValues () {
777
804
  /**
778
805
  * Descrição:
779
806
  * Exibe o texto quando:
780
- * - Nao esta carregando a coluna
781
- * - Nao tem itens na coluna
782
- * - Nao estou fazendo o drag and drop
783
- * - Nao esta exibindo o dialog de confirmação
807
+ * - Não está carregando a coluna
808
+ * - Não tem itens na coluna
809
+ * - Não estou fazendo o drag and drop
810
+ * - Não está exibindo o dialog de confirmação
784
811
  *
785
812
  * @param {Object} header
786
813
  */
@@ -881,8 +908,6 @@ function setSortable (element, index) {
881
908
 
882
909
  group: useOnlyDragAndDropY ? `column-${index}` : 'shared',
883
910
 
884
- // direction: 'vertical',
885
-
886
911
  /**
887
912
  * invertSwap muda o algoritmo de swap: em vez de trocar quando o centro do item arrastado
888
913
  * cruza o centro do alvo (instável, causa oscilação), troca apenas quando cruza a BORDA
@@ -944,7 +969,10 @@ function stopDragging () {
944
969
  /**
945
970
  * Intercepta cada chamada de scroll do SortableJS.
946
971
  * - Scroll vertical (colunas): retorna 'continue' para manter o comportamento nativo.
947
- * - Scroll horizontal (container do board): aplica smooth scroll via rAF.
972
+ * - Scroll horizontal (container do board): aplica smooth scroll interpolado via rAF.
973
+ *
974
+ * rAF (requestAnimationFrame) sincroniza com o refresh da tela (~60fps),
975
+ * tornando o scroll suave. Sem rAF, seria um "salto" abrupto.
948
976
  */
949
977
  function handleSortableScroll (offsetX, offsetY, evt, _touchEvt, scrollEl) {
950
978
  // Obtém o container com scroll horizontal (`.qas-grabbable__container`).
@@ -961,16 +989,25 @@ function handleSortableScroll (offsetX, offsetY, evt, _touchEvt, scrollEl) {
961
989
 
962
990
  smoothScrollTarget = Math.max(0, Math.min(smoothScrollTarget + offsetX, maxScroll))
963
991
 
992
+ // Se não há animação em andamento, inicia uma.
993
+ // rAF (requestAnimationFrame) agenda a função smoothScrollStep para o próximo frame (~16ms a 60fps).
994
+
995
+ /**
996
+ * Se não há animação em andamento, inicia uma. rAF (requestAnimationFrame) agenda a função smoothScrollStep
997
+ * para o próximo frame (~16ms a 60fps).
998
+ */
964
999
  if (!smoothScrollRafId) {
965
1000
  smoothScrollRafId = requestAnimationFrame(smoothScrollStep)
966
1001
  }
1002
+
967
1003
  return
968
1004
  }
969
1005
 
970
- // Scroll vertical (dentro da coluna): aplica manualmente com velocidade independente do scrollSpeed horizontal.
971
- // offsetY vem escalado pelo scrollSpeed (60), então normalizamos para VERTICAL_SCROLL_SPEED (10).
972
- // Além disso, como scrollSensitivity está em HORIZONTAL_SCROLL_SENSITIVITY (200), verificamos manualmente
973
- // se o cursor está dentro de VERTICAL_SCROLL_SENSITIVITY (80) da borda da coluna antes de rolar.
1006
+ /**
1007
+ * SortableJS chama essa função com offsets de scroll.
1008
+ * Para scroll vertical: retorna 'continue' para deixar o comportamento nativo.
1009
+ * Para scroll horizontal: aplica smooth scroll customizado via rAF.
1010
+ */
974
1011
  if (offsetY) {
975
1012
  const clientY = evt?.clientY ?? evt?.touches?.[0]?.clientY
976
1013
 
@@ -991,6 +1028,14 @@ function handleSortableScroll (offsetX, offsetY, evt, _touchEvt, scrollEl) {
991
1028
  /**
992
1029
  * Loop de animação que interpola o scrollLeft em direção ao target
993
1030
  * usando fator de lerp (move 20% da distância restante por frame).
1031
+ *
1032
+ * Funciona assim:
1033
+ * 1. Calcula a diferença entre a posição atual e a posição desejada (target).
1034
+ * 2. Se ainda há diferença significativa, move 20% da distância restante.
1035
+ * 3. Agenda o próximo frame via rAF para continuar a interpolação.
1036
+ * 4. Quando chega perto o suficiente (~0.5px), para a animação.
1037
+ *
1038
+ * Resultado: scroll suave e progressivo, não um "salto" abrupto.
994
1039
  */
995
1040
  function smoothScrollStep () {
996
1041
  smoothScrollRafId = null
@@ -1000,16 +1045,21 @@ function smoothScrollStep () {
1000
1045
  const current = smoothScrollEl.scrollLeft
1001
1046
  const diff = smoothScrollTarget - current
1002
1047
 
1048
+ // Se chegou bem perto do target, para na posição exata.
1003
1049
  if (Math.abs(diff) < 0.5) {
1004
1050
  smoothScrollEl.scrollLeft = smoothScrollTarget
1005
1051
  return
1006
1052
  }
1007
1053
 
1054
+ // Move 20% da distância restante (interpolação suave).
1008
1055
  smoothScrollEl.scrollLeft = current + diff * SMOOTH_SCROLL_LERP
1056
+
1057
+ // Agenda o próximo frame para continuar a animação (chamada recursiva via rAF).
1009
1058
  smoothScrollRafId = requestAnimationFrame(smoothScrollStep)
1010
1059
  }
1011
1060
 
1012
1061
  function resetSmoothScroll () {
1062
+ // Cancela a animação agendada (rAF) para evitar que continue executando após o drag terminar.
1013
1063
  if (smoothScrollRafId) {
1014
1064
  cancelAnimationFrame(smoothScrollRafId)
1015
1065
  smoothScrollRafId = null
@@ -1051,7 +1101,7 @@ function addItemToList ({ headerKey, item, index }) {
1051
1101
 
1052
1102
  updatedList.splice(insertIndex, 0, item)
1053
1103
 
1054
- columnsResultsModel.value[headerKey] = props.useMarkRaw ? markRaw(updatedList) : updatedList
1104
+ columnsResultsModel.value[headerKey] = updatedList
1055
1105
  columnsPagination.value[headerKey].count += 1
1056
1106
  }
1057
1107
 
@@ -1172,7 +1222,7 @@ function removeItemFromList ({ headerKey, itemId }) {
1172
1222
  const updatedList = [...columnItemList]
1173
1223
  updatedList.splice(itemIndex, 1)
1174
1224
 
1175
- columnsResultsModel.value[headerKey] = props.useMarkRaw ? markRaw(updatedList) : updatedList
1225
+ columnsResultsModel.value[headerKey] = updatedList
1176
1226
 
1177
1227
  /**
1178
1228
  * Remove o item do count da coluna para não mostrar o botão de "Ver mais¨.
@@ -1180,13 +1230,29 @@ function removeItemFromList ({ headerKey, itemId }) {
1180
1230
  columnsPagination.value[headerKey].count -= 1
1181
1231
  }
1182
1232
 
1233
+ /**
1234
+ * Substitui um item na lista de uma coluna pelos dados atualizados do servidor.
1235
+ *
1236
+ * 1. Localiza a lista de itens da coluna
1237
+ * 2. Encontra o índice do item usando o itemIdKey como identificador
1238
+ * 3. Se encontrado, altera o item localmente (sem criar nova referência de array)
1239
+ *
1240
+ * @param {Object} params
1241
+ * @param {string} params.headerKey - ID da coluna
1242
+ * @param {string|number} params.itemId - ID do item a atualizar (deve corresponder a props.itemIdKey)
1243
+ * @param {Object} params.updatedItem - Novo objeto do item com dados atualizados
1244
+ *
1245
+ */
1183
1246
  function updateItemInList ({ headerKey, itemId, updatedItem }) {
1184
1247
  const columnItemList = columnsResultsModel.value[headerKey]
1185
1248
 
1249
+ // Encontra o índice do item na lista usando o identificador (itemIdKey)
1186
1250
  const itemIndex = columnItemList.findIndex(itemContent => itemContent[props.itemIdKey] === itemId)
1187
1251
 
1252
+ // Se não encontrar (itemIndex === -1), retorna sem fazer nada
1188
1253
  if (!~itemIndex) return
1189
1254
 
1255
+ // Substitui o item antigo pelo item atualizado na mesma posição
1190
1256
  columnItemList.splice(itemIndex, 1, updatedItem)
1191
1257
  }
1192
1258
 
@@ -1209,7 +1275,7 @@ async function updatePosition ({ newHeaderKey, oldHeaderKey, itemId, event, opti
1209
1275
 
1210
1276
  const isFnUpdatePositionUrl = typeof props.updatePositionUrl === 'function'
1211
1277
 
1212
- isLoadingFromSeeMore.value = true
1278
+ hideSkeleton.value = true
1213
1279
 
1214
1280
  const url = isFnUpdatePositionUrl
1215
1281
  ? props.updatePositionUrl({ newHeaderKey, oldHeaderKey, itemId })
@@ -1314,7 +1380,7 @@ function isCancelledError (error) {
1314
1380
  * @param {string|number} params.itemId - ID do item a ser movido (valor correspondente à prop `itemIdKey`).
1315
1381
  * @param {string|number} params.fromColumnId - ID da coluna de origem (valor correspondente à prop `columnIdKey`).
1316
1382
  * @param {string|number} params.toColumnId - ID da coluna de destino (valor correspondente à prop `columnIdKey`).
1317
- * @param {Object} [params.updatedItem] - Dados atualizados do item. Quando informado, sobrescreve o item original na coluna de destino.
1383
+ * @param {Object} [params.updatedItem] - Dados atualizados do item. Quando informado, sobrescreve o item original na coluna de destino.
1318
1384
  * @returns {void}
1319
1385
  */
1320
1386
  function transferItemToColumn ({ itemId, fromColumnId, toColumnId, updatedItem }) {
@@ -1334,10 +1400,10 @@ function transferItemToColumn ({ itemId, fromColumnId, toColumnId, updatedItem }
1334
1400
  function hasSkeletonByHeader (header) {
1335
1401
  const headerKey = getKeyByHeader(header)
1336
1402
 
1337
- return (props.skeleton || columnsLoading.value[headerKey]) && !isLoadingFromSeeMore.value
1403
+ return (props.skeleton || columnsLoading.value[headerKey]) && !hideSkeleton.value
1338
1404
  }
1339
1405
 
1340
- function getColumnClass (header) {
1406
+ function getColumnClasses (header) {
1341
1407
  const headerKey = getKeyByHeader(header)
1342
1408
 
1343
1409
  return {
@@ -97,8 +97,8 @@ props:
97
97
  type: Number
98
98
  default: 12
99
99
 
100
- use-mark-raw:
101
- desc: Define se os valores dos itens das colunas irão ser atribuídos utilizando o "markRaw", onde somente a primeira camada do model será reativa. (https://vuejs.org/api/reactivity-advanced.html#markraw)
100
+ skeleton:
101
+ desc: Exibe o estado de esqueleto de carregamento das colunas. Enquanto `true`, cada coluna renderiza cards fictícios no lugar dos itens reais.
102
102
  type: Boolean
103
103
  default: true
104
104
 
@@ -150,11 +150,26 @@ slots:
150
150
  column-item:
151
151
  desc: Slot para acessar o item da coluna.
152
152
  scope:
153
+ column-index:
154
+ desc: Índice da coluna atual.
155
+ type: Number
156
+ default: 0
157
+
153
158
  fields:
154
159
  desc: Fields referente à coluna atual do template.
155
160
  type: Object
156
161
  default: {}
157
162
 
163
+ header:
164
+ desc: Informações do header da coluna atual.
165
+ type: Object
166
+ default: {}
167
+
168
+ is-updating-position:
169
+ desc: Indica se o item está sendo atualizado via drag-and-drop (aguardando retorno da API de update).
170
+ type: Boolean
171
+ default: false
172
+
158
173
  item:
159
174
  desc: Informações de cada item da coluna que foi buscado através da API.
160
175
  type: Object
@@ -220,6 +235,45 @@ methods:
220
235
  'fetchColumn: (header) => void':
221
236
  desc: Busca uma coluna específica com base no header fornecido.
222
237
 
238
+ 'refreshColumn: (header) => void':
239
+ desc: Reinicia a paginação da coluna e refaz a busca, substituindo completamente os itens existentes.
240
+ params:
241
+ header:
242
+ desc: Header da coluna a ser recarregada (mesmo formato da prop `headers`).
243
+ type: Object
244
+ required: true
245
+
246
+ 'removeItemFromList: ({ headerKey, itemId }) => void':
247
+ desc: Remove um item da lista de uma coluna localmente, sem realizar nenhuma requisição. Também decrementa o contador de paginação.
248
+ params:
249
+ headerKey:
250
+ desc: Chave identificadora da coluna de origem (valor correspondente à prop `column-id-key`).
251
+ type: String | Number
252
+ required: true
253
+ itemId:
254
+ desc: ID do item a ser removido (valor correspondente à prop `item-id-key`).
255
+ type: String | Number
256
+ required: true
257
+
258
+ 'updateItemInList: ({ headerKey, itemId, updatedItem }) => void':
259
+ desc: Substitui os dados de um item existente em uma coluna localmente, sem realizar nenhuma requisição.
260
+ params:
261
+ headerKey:
262
+ desc: Chave identificadora da coluna (valor correspondente à prop `column-id-key`).
263
+ type: String | Number
264
+ required: true
265
+ itemId:
266
+ desc: ID do item a ser atualizado (valor correspondente à prop `item-id-key`).
267
+ type: String | Number
268
+ required: true
269
+ updatedItem:
270
+ desc: Dados atualizados que substituirão o item original.
271
+ type: Object
272
+ required: true
273
+
274
+ 'refetchColumns: () => void':
275
+ desc: Alias de `fetchColumns`. Busca novamente todas as colunas com base nos headers fornecidos.
276
+
223
277
  'transferItemToColumn: ({ itemId, fromColumnId, toColumnId, updatedItem? }) => void':
224
278
  desc: |
225
279
  Transfere um item de uma coluna para outra localmente, sem realizar nenhuma requisição.
@@ -5,7 +5,6 @@
5
5
  </template>
6
6
 
7
7
  <script setup>
8
-
9
8
  import setScrollGradient from '../../../helpers/set-scroll-gradient'
10
9
 
11
10
  import { ref, onMounted, onBeforeUnmount } from 'vue'
@@ -32,3 +32,8 @@ props:
32
32
  use-spacing:
33
33
  desc: Controla espaçamento do componente.
34
34
  type: Boolean
35
+
36
+ provide:
37
+ is-box:
38
+ desc: Identificador booleano que indica quando um componente está dentro do contexto de um QasBox.
39
+ type: Boolean
@@ -34,6 +34,7 @@
34
34
 
35
35
  <div class="qas-card__content relative-position" :class="contentClasses">
36
36
  <qas-skeleton v-if="props.skeleton" height="100px" />
37
+
37
38
  <slot v-else name="default" />
38
39
  </div>
39
40
 
@@ -165,6 +165,12 @@ const hasDatePicker = computed(() => !props.useTimeOnly && !props.readonly)
165
165
  const hasTimePicker = computed(() => !props.useDateOnly && !props.readonly)
166
166
 
167
167
  watch(() => props.modelValue, (current, original) => {
168
+ /**
169
+ * No caso de ser adicionado uma data incompleta ou inválida, vamos limpar o model,
170
+ * mas não vamos limpar o "currentValue", para não limpar o campo, somente o model.
171
+ */
172
+ if (!current && original && error.value) return
173
+
168
174
  if (!current || props.useTimeOnly) {
169
175
  currentValue.value = current
170
176
  return
@@ -217,6 +223,9 @@ function updateModelValue (value) {
217
223
  if (error.value) {
218
224
  hasInvalidDate.value = true
219
225
  errorMessage.value = 'Data inválida.'
226
+
227
+ emit('update:modelValue', '')
228
+
220
229
  return
221
230
  }
222
231
 
@@ -262,18 +271,33 @@ function validateDateAndTime (value) {
262
271
  function validateDateTimeOnBlur () {
263
272
  const valueLength = currentValue.value?.replace?.(/_/g, '')?.length
264
273
 
274
+ // Caso for datetime
275
+ if (!props.useDateOnly && !props.useTimeOnly) {
276
+ const [date, time] = (currentValue.value || '').split(' ') || []
277
+ const isValidDate = date?.replace?.(/_/g, '')?.length === props.dateMask.length
278
+
279
+ /**
280
+ * Caso tenha preenchido a data, a data seja válida e não tenha preenchido o horário,
281
+ * preenche o horário com 00:00, ou de acordo com a mask.
282
+ */
283
+ if (isValidDate && !validateDateOnly(date) && !time?.length) {
284
+ // Horario setado deve seguir a mascara.
285
+ const defaultTimeByMask = props.timeMask.replace(/[HhMmSs]/g, '0')
286
+ const newValue = `${date} ${defaultTimeByMask}`
287
+
288
+ currentValue.value = newValue
289
+ updateModelValue(newValue)
290
+
291
+ return
292
+ }
293
+ }
294
+
265
295
  // valida se o tamanho digitado é o tamanho que a mascara espera receber
266
296
  error.value = !!((valueLength < mask.value.length || error.value) && valueLength)
267
297
 
268
298
  if (error.value && !hasInvalidDate.value) {
269
299
  errorMessage.value = 'Data incompleta.'
270
- }
271
-
272
- if (hasInvalidDate.value) {
273
- currentValue.value = ''
274
- }
275
300
 
276
- if (error.value || hasInvalidDate.value) {
277
301
  emit('update:modelValue', '')
278
302
  }
279
303
  }
@@ -5,10 +5,8 @@
5
5
  <slot name="header">
6
6
  <div class="items-center justify-between row">
7
7
  <qas-label data-cy="dialog-title" :label="props.card.title" margin="none">
8
- <slot name="title"></slot>
8
+ <slot name="title" />
9
9
  </qas-label>
10
- <!-- <slot name="title">
11
- </slot> -->
12
10
 
13
11
  <qas-btn v-if="isInfoDialog" v-close-popup color="grey-10" data-cy="dialog-close-btn" icon="sym_r_close" variant="tertiary" />
14
12
  </div>
@@ -69,6 +69,11 @@ slots:
69
69
  header:
70
70
  desc: Slot para o título.
71
71
 
72
+ provide:
73
+ is-dialog:
74
+ desc: Identificador booleano que indica quando um componente está dentro do contexto de um QasDialog.
75
+ type: Boolean
76
+
72
77
  events:
73
78
  '@update:model-value -> function (value)':
74
79
  desc: Dispara toda vez que o model é atualizado, também utilizado para v-model.
@@ -111,6 +116,9 @@ selectors:
111
116
  desc: Seletor do botão de confirmar do componente.
112
117
  examples: ['data-cy="dialog-ok-btn"']
113
118
 
119
+ title:
120
+ desc: Slot para inserir conteúdo adicional ao lado do título do card do dialog.
121
+
114
122
  dialog-title:
115
123
  desc: Seletor do título do componente.
116
124
  examples: ['data-cy="dialog-title"']
@@ -62,6 +62,11 @@ slots:
62
62
  content:
63
63
  desc: Slot para substituir o conteúdo principal do card.
64
64
 
65
+ provide:
66
+ is-expansion-item:
67
+ desc: Identificador booleano que indica quando um componente está dentro do contexto de um QasExpansionItem.
68
+ type: Boolean
69
+
65
70
  events:
66
71
  '@update:model-value -> function(value)':
67
72
  desc: Dispara quando o model-value altera, também usado para v-model.
@@ -120,6 +120,11 @@ slots:
120
120
  'legend-bottom[nome-da-chave-do-fieldset]-[nome-da-chave-do-subset]':
121
121
  desc: Acessa o slot da seção abaixo do conteúdo do form de um subset específico.
122
122
 
123
+ provide:
124
+ is-form-generator:
125
+ desc: Identificador booleano que indica quando um componente está dentro do contexto de um QasFormGenerator.
126
+ type: Boolean
127
+
123
128
  events:
124
129
  '@update:model-value -> function(value)':
125
130
  desc: Dispara quando o model-value altera, também usado para v-model.
@@ -54,6 +54,11 @@ props:
54
54
  type: Boolean
55
55
  default: false
56
56
 
57
+ provide:
58
+ is-header:
59
+ desc: Identificador booleano que indica quando um componente está dentro do contexto de um QasHeader.
60
+ type: Boolean
61
+
57
62
  slots:
58
63
  actions:
59
64
  desc: slot para acessar seção da direita (ações).
@@ -30,35 +30,28 @@ import { ref, onMounted, onBeforeUnmount, useSlots, nextTick, watch } from 'vue'
30
30
 
31
31
  defineOptions({ name: 'QasLazyLoadingComponents' })
32
32
 
33
- // const emit = defineEmits(['update:visibleItems'])
34
-
35
33
  const props = defineProps({
36
- // Porcentagem de visibilidade necessária para ativar (0.0 a 1.0)
37
34
  threshold: {
38
35
  type: Number,
39
36
  default: 0.1 // 10% visível
40
37
  },
41
38
 
42
- // Margem extra ao redor do viewport para pré-carregamento
43
39
  rootMargin: {
44
40
  type: String,
45
41
  default: '0px'
46
42
  },
47
43
 
48
- // Direção do scroll: 'vertical' (padrão) ou 'horizontal'
49
44
  direction: {
50
45
  type: String,
51
46
  default: 'vertical',
52
47
  validator: value => ['vertical', 'horizontal'].includes(value)
53
48
  },
54
49
 
55
- // Altura dos placeholders antes de carregar (usado em direction='vertical')
56
50
  placeholderHeight: {
57
51
  type: String,
58
52
  default: '500px'
59
53
  },
60
54
 
61
- // Largura dos placeholders antes de carregar (usado em direction='horizontal')
62
55
  placeholderWidth: {
63
56
  type: String,
64
57
  default: '300px'
@@ -4,21 +4,46 @@ meta:
4
4
  desc: Componente para carregar elementos do slot somente quando ficam visíveis na viewport, otimizando performance da página.
5
5
 
6
6
  props:
7
- threshold:
8
- desc: Threshold do IntersectionObserver (0 = aparece um pouco, 1 = totalmente visível)
9
- default: 0.1
10
- type: Number
11
-
12
- root-margin:
13
- desc: Margem extra ao redor do viewport para pré-carregamento
14
- default: '0px'
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.
15
9
  type: String
10
+ default: vertical
11
+ values: vertical | horizontal
16
12
 
17
13
  placeholder-height:
18
- desc: Altura do placeholder exibido enquanto o elemento não está visível
14
+ desc: Altura do placeholder exibido enquanto o elemento não está visível (usado quando `direction` é `vertical`).
19
15
  default: '500px'
20
16
  type: String
21
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
+
22
39
  slots:
23
40
  default:
24
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
@@ -229,7 +229,7 @@ function getDialogSlot (name) {
229
229
 
230
230
  // ------------------------- composable functions ------------------------------
231
231
  function useList () {
232
- const filteredOptions = ref(props.options)
232
+ const filteredOptions = ref([...props.options])
233
233
 
234
234
  const selectedOptions = computed(() => {
235
235
  const options = []
@@ -206,6 +206,27 @@ function previous () {
206
206
  border-radius: var(--qas-generic-border-radius);
207
207
  }
208
208
 
209
+ .q-stepper__tab {
210
+ /*
211
+ * Por padrão o quasar sempre deixa as demais linhas com o mesmo tamanho, exceto a primeira e a última, que são
212
+ * maiores devido terem alinhamento inicial e final respectivamente, enquanto as demais possuem alinhamento
213
+ * central. Para equalizar visualmente os tamanhos, é necessário setar uma proporção de flex diferente para a
214
+ * primeira/última tab e para as tabs do meio.
215
+ *
216
+ * Proporção 2:3 para equalizar visualmente os tamanhos:
217
+ * - Primeira/última tab: flex 2 (possuem apenas meia linha)
218
+ * - Tabs do meio: flex 3 (possuem linha completa dos dois lados)
219
+ */
220
+ &:first-child,
221
+ &:last-child {
222
+ flex: 2;
223
+ }
224
+
225
+ &:not(:first-child):not(:last-child) {
226
+ flex: 3;
227
+ }
228
+ }
229
+
209
230
  .q-stepper {
210
231
  &__tab {
211
232
  &--done {
@@ -223,9 +244,10 @@ function previous () {
223
244
  }
224
245
  }
225
246
 
226
- // Seta a cor do after da linha quando a step está finalizada, é a primeira step ou a próxima é ativa.
247
+ // Seta a cor do after da linha quando a step está finalizada, é a primeira step ou a próxima é ativa ou finalizada.
227
248
  &:first-child,
228
- &:has(+ .q-stepper__tab.q-stepper__tab--active) {
249
+ &:has(+ .q-stepper__tab.q-stepper__tab--active),
250
+ &:has(+ .q-stepper__tab.q-stepper__tab--done) {
229
251
  .q-stepper__line::after {
230
252
  background-color: var(--q-primary);
231
253
  }
@@ -7,8 +7,12 @@ import { computed, reactive } from 'vue'
7
7
  * isSmall: boolean,
8
8
  * isMedium: boolean,
9
9
  * isLarge: boolean,
10
+ * isXLarge: boolean,
11
+ * is2XLarge: boolean,
10
12
  * untilMedium: boolean,
11
13
  * untilLarge: boolean,
14
+ * untilXLarge: boolean,
15
+ * until2XLarge: boolean,
12
16
  * isMobile: boolean
13
17
  * }}
14
18
  *
@@ -25,15 +29,27 @@ export default function () {
25
29
  // de 600 até 1023px
26
30
  isMedium: computed(() => Screen.sm),
27
31
 
28
- // de 600 até 1023px
32
+ // Maior que 1023px
29
33
  isLarge: computed(() => Screen.gt.sm),
30
34
 
35
+ // Maior que 1439px
36
+ isXLarge: computed(() => Screen.gt.md),
37
+
38
+ // Maior que 1919px
39
+ is2XLarge: computed(() => Screen.gt.lg),
40
+
31
41
  // de 0 até 599px
32
42
  untilMedium: computed(() => Screen.lt.sm),
33
43
 
34
44
  // de 0 ate 1023px
35
45
  untilLarge: computed(() => Screen.lt.md),
36
46
 
47
+ // de 0 até 1439px
48
+ untilXLarge: computed(() => Screen.lt.lg),
49
+
50
+ // de 0 até 1919px
51
+ until2XLarge: computed(() => Screen.lt.xl),
52
+
37
53
  // Plataforma
38
54
  isMobile: computed(() => Platform.is.mobile || false)
39
55
  })
@@ -160,7 +160,7 @@ function parseValue (value) {
160
160
  try { return JSON.parse(value) } catch { return value }
161
161
  }
162
162
 
163
- function booleanLabel (value, trueLabel = 'sim', falseLabel = 'não') {
163
+ function booleanLabel (value, trueLabel = 'Sim', falseLabel = 'Não') {
164
164
  try { return JSON.parse(value) ? trueLabel : falseLabel } catch { return value }
165
165
  }
166
166
 
@@ -174,13 +174,16 @@ export default function setScrollGradient (config = {}) {
174
174
  const elementRect = element.getBoundingClientRect()
175
175
  const parentRect = element.parentElement.getBoundingClientRect()
176
176
 
177
+ // pequena margem para garantir que o gradiente fique grudado ao elemento.
178
+ const safeArea = 1
179
+
177
180
  /**
178
181
  * diferença entre o bottom do pai e o bottom do filho, valor positivo significa
179
182
  * que há espaço livre abaixo do filho.
180
183
  */
181
184
  const distance = {
182
- end: isVertical ? (parentRect.bottom - elementRect.bottom) : (parentRect.right - elementRect.right),
183
- start: isVertical ? (elementRect.top - parentRect.top) : (elementRect.left - parentRect.left)
185
+ end: isVertical ? (parentRect.bottom - elementRect.bottom - safeArea) : (parentRect.right - elementRect.right - safeArea),
186
+ start: isVertical ? (elementRect.top - parentRect.top) - safeArea : (elementRect.left - parentRect.left) - safeArea
184
187
  }
185
188
 
186
189
  span.style[getDirection(direction)] = `${distance[direction]}px`
@@ -98,7 +98,7 @@ export default {
98
98
 
99
99
  this.mx_cachedOptions = []
100
100
 
101
- this.mx_filterOptionsByStore('')
101
+ if (!this.disable) this.mx_filterOptionsByStore('')
102
102
 
103
103
  setTimeout(() => this.$emit('update:modelValue', undefined))
104
104
  }
@@ -3,7 +3,7 @@ type: component
3
3
  meta:
4
4
  desc: Plugin que implementa a action `destroy` do `StoreModule` adicionando comportamento de confirmação antes de excluir, este mesmo plugin é utilizado no componente `QasDelete`.
5
5
 
6
- inject:
6
+ provide:
7
7
  'this.$qas.delete(config)':
8
8
  params:
9
9
  config:
@@ -3,7 +3,7 @@ type: component
3
3
  meta:
4
4
  desc: Plugin que implementa o `QasDialog`.
5
5
 
6
- inject:
6
+ provide:
7
7
  'this.$qas.dialog(config)':
8
8
  params:
9
9
  config:
@@ -3,7 +3,7 @@ type: component
3
3
  meta:
4
4
  desc: Plugin que implementa o "QNotify" do quasar.
5
5
 
6
- inject:
6
+ provide:
7
7
  'this.$qas.error(msg)':
8
8
  params:
9
9
  msg:
@@ -3,7 +3,7 @@ type: component
3
3
  meta:
4
4
  desc: Plugin que implementa o "QNotify" do quasar para notificações de sucesso.
5
5
 
6
- inject:
6
+ provide:
7
7
  'this.$qas.success(msg)':
8
8
  params:
9
9
  msg:
@@ -4,15 +4,31 @@ export default () => {
4
4
  const screensModel = {
5
5
  // até 599px
6
6
  isSmall: () => Screen.xs,
7
+
7
8
  // de 600 até 1023px
8
9
  isMedium: () => Screen.sm,
9
- // de 600 até 1023px
10
+
11
+ // Maior que 1023px
10
12
  isLarge: () => Screen.gt.sm,
13
+
14
+ // Maior que 1439px
15
+ isXLarge: () => Screen.gt.md,
16
+
17
+ // Maior que 1919px
18
+ is2XLarge: () => Screen.gt.lg,
19
+
11
20
  // de 0 até 599px
12
21
  untilMedium: () => Screen.lt.sm,
22
+
13
23
  // de 0 ate 1023px
14
24
  untilLarge: () => Screen.lt.md,
15
25
 
26
+ // de 0 até 1439px
27
+ untilXLarge: () => Screen.lt.lg,
28
+
29
+ // de 0 até 1919px
30
+ until2XLarge: () => Screen.lt.xl,
31
+
16
32
  // Plataforma
17
33
  isMobile: () => Platform.is.mobile || false
18
34
  }
@@ -3,7 +3,7 @@ type: component
3
3
  meta:
4
4
  desc: Plugin que implementa o "Screen" do quasar.
5
5
 
6
- inject:
6
+ provide:
7
7
  'this.$qas.screen':
8
8
  type: Object
9
9
  debugger: true
@@ -11,6 +11,10 @@ inject:
11
11
  isSmall: false
12
12
  isMedium: false
13
13
  isLarge: false
14
+ isXLarge: false
15
+ is2XLarge: false
14
16
  untilMedium: false
15
17
  untilLarge: false
18
+ untilXLarge: false
19
+ until2XLarge: false
16
20
  isMobile: false