@bildvitta/quasar-ui-asteroid 3.17.0 → 3.18.0-beta.1

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 (65) hide show
  1. package/package.json +1 -1
  2. package/src/components/actions-menu/QasActionsMenu.vue +3 -3
  3. package/src/components/alert/QasAlert.vue +187 -60
  4. package/src/components/alert/QasAlert.yml +27 -5
  5. package/src/components/app-bar/QasAppBar.vue +2 -0
  6. package/src/components/app-menu/QasAppMenu.vue +127 -68
  7. package/src/components/app-menu/QasAppMenu.yml +10 -0
  8. package/src/components/app-user/QasAppUser.vue +12 -8
  9. package/src/components/app-user/QasAppUser.yml +5 -0
  10. package/src/components/badge/QasBadge.vue +1 -1
  11. package/src/components/checkbox/QasCheckbox.vue +97 -32
  12. package/src/components/date-time-input/QasDateTimeInput.vue +2 -2
  13. package/src/components/error-message/QasErrorMessage.vue +23 -0
  14. package/src/components/error-message/QasErrorMessage.yml +9 -0
  15. package/src/components/expansion-item/QasExpansionItem.vue +14 -16
  16. package/src/components/filters/QasFilters.vue +51 -30
  17. package/src/components/filters/QasFilters.yml +9 -0
  18. package/src/components/gallery/QasGallery.vue +2 -3
  19. package/src/components/gallery-card/QasGalleryCard.vue +43 -12
  20. package/src/components/gallery-card/QasGalleryCard.yml +22 -6
  21. package/src/components/input/QasInput.vue +3 -3
  22. package/src/components/label/QasLabel.vue +1 -1
  23. package/src/components/list-view/QasListView.vue +6 -1
  24. package/src/components/list-view/QasListView.yml +5 -0
  25. package/src/components/nested-fields/QasNestedFields.vue +10 -2
  26. package/src/components/nested-fields/QasNestedFields.yml +18 -3
  27. package/src/components/password-input/QasPasswordInput.vue +6 -2
  28. package/src/components/radio/QasRadio.vue +56 -10
  29. package/src/components/radio/QasRadio.yml +8 -1
  30. package/src/components/search-input/QasSearchInput.vue +14 -29
  31. package/src/components/select/QasSelect.vue +31 -21
  32. package/src/components/select-filter/QasSelectFilter.vue +33 -6
  33. package/src/components/select-filter/QasSelectFilter.yml +5 -0
  34. package/src/components/select-list/QasSelectList.vue +6 -6
  35. package/src/components/select-list/private/PvSelectListCheckbox.vue +1 -1
  36. package/src/components/table-generator/QasTableGenerator.vue +10 -5
  37. package/src/components/table-generator/QasTableGenerator.yml +9 -4
  38. package/src/components/toggle/QasToggle.vue +26 -1
  39. package/src/components/toggle-visibility/QasToggleVisibility.vue +15 -6
  40. package/src/components/tree-generator/QasTreeGenerator.vue +10 -2
  41. package/src/components/uploader/QasUploader.vue +7 -14
  42. package/src/components/uploader/private/PvUploaderGalleryCard.vue +2 -2
  43. package/src/composables/private/index.js +3 -2
  44. package/src/composables/private/use-error-message.js +28 -0
  45. package/src/composables/use-default-filters.js +47 -15
  46. package/src/css/components/field.scss +69 -2
  47. package/src/css/components/index.scss +1 -3
  48. package/src/css/components/item.scss +3 -2
  49. package/src/css/components/menu.scss +21 -0
  50. package/src/css/mixins/index.scss +1 -0
  51. package/src/css/mixins/set-error-message.scss +8 -0
  52. package/src/css/plugins/notify.scss +37 -37
  53. package/src/css/variables/scrollbar.scss +1 -1
  54. package/src/enums/Status.js +3 -3
  55. package/src/helpers/colors.js +137 -0
  56. package/src/helpers/index.js +1 -0
  57. package/src/helpers/set-scroll-gradient.js +261 -0
  58. package/src/plugins/notify-error/NotifyError.js +2 -1
  59. package/src/plugins/notify-success/NotifySuccess.js +2 -1
  60. package/src/vue-plugin.js +3 -3
  61. package/src/components/info/QasInfo.vue +0 -155
  62. package/src/components/info/QasInfo.yml +0 -34
  63. package/src/css/components/checkbox.scss +0 -14
  64. package/src/css/components/radio.scss +0 -18
  65. package/src/css/components/toggle.scss +0 -13
@@ -0,0 +1,261 @@
1
+ import { uid } from 'quasar'
2
+ import { convertToRgb } from './colors'
3
+
4
+ /**
5
+ * @typedef {'start'|'end'} Direction
6
+ */
7
+
8
+ /**
9
+ * Helper para adicionar um gradiente de scroll em um elemento.
10
+ *
11
+ * @param {object} config
12
+ * @param {object} config.styles
13
+ * @param {string} config.styles.color - precisa ser uma cor RGB|HEX|RGBA
14
+ * @param {string} config.styles.size - tamanho do gradient (mudar somente quando for necessário)
15
+ * @param {'y'|'x'} config.orientation - direção do scroll (vertical ou horizontal)
16
+ */
17
+ export default function setScrollGradient (config = {}) {
18
+ const { styles, orientation = 'y' } = config
19
+ const { color = '#FFFFFF', size = '40px' } = styles || {}
20
+
21
+ const { r, g, b } = convertToRgb(color) || {}
22
+ const rgbParam = `${r}, ${g}, ${b}`
23
+
24
+ const isVertical = orientation === 'y'
25
+
26
+ let uuid = ''
27
+ let resizeObserver = null
28
+
29
+ // functions
30
+ /**
31
+ * Toda vez que "initializeScrollGradient" for chamado, remove o gradient anterior
32
+ * e cria um novo.
33
+ *
34
+ * @param {HTMLElement} element
35
+ */
36
+ function initializeScrollGradient (element) {
37
+ // remove o gradient anterior caso exista
38
+ removeScrollGradient(element)
39
+
40
+ uuid = uid()
41
+
42
+ // cria os spans de gradiente
43
+ createSpan({ direction: 'start', element })
44
+ createSpan({ direction: 'end', element })
45
+
46
+ // verifica se o elemento tem scroll no final
47
+ if (hasEndScroll(element)) toggleSpan('end', true)
48
+
49
+ // adiciona o listener de scroll
50
+ element.addEventListener('scroll', () => toggleSpanByScrollPosition(element))
51
+ setResizeObserver(element)
52
+ }
53
+
54
+ /**
55
+ * Remove:
56
+ * - event listener
57
+ * - resize observer
58
+ * - spans
59
+ * - uuid
60
+ *
61
+ * @parm {HTMLElement} element
62
+ */
63
+ function removeScrollGradient (element) {
64
+ if (!uuid) return
65
+
66
+ element.removeEventListener('scroll', () => toggleSpanByScrollPosition(element))
67
+ resizeObserver?.unobserve(element)
68
+
69
+ // remove scroll gradient spans
70
+ const scrollSpanTop = getSpan('start')
71
+ const scrollSpanBottom = getSpan('end')
72
+
73
+ if (scrollSpanTop) scrollSpanTop.remove()
74
+ if (scrollSpanBottom) scrollSpanBottom.remove()
75
+
76
+ uuid = ''
77
+ }
78
+
79
+ /**
80
+ * mostra ou esconde o span do gradient.
81
+ *
82
+ * @param {Direction} direction
83
+ * @param {boolean} show
84
+ */
85
+ function toggleSpan (direction, show) {
86
+ const span = getSpan(direction)
87
+
88
+ if (!span) return
89
+
90
+ span.style.display = show ? 'block' : 'none'
91
+ }
92
+
93
+ /**
94
+ * @param {Direction} direction
95
+ */
96
+ function getSpan (direction) {
97
+ return document.getElementById(`scroll-gradient-${direction}-${uuid}`)
98
+ }
99
+
100
+ /**
101
+ * cria o span do gradient.
102
+ *
103
+ * @param {{
104
+ * direction: Direction,
105
+ * element: HTMLElement
106
+ * }}
107
+ */
108
+ function createSpan ({ direction, element }) {
109
+ const span = document.createElement('span')
110
+
111
+ // styles
112
+ Object.assign(span.style, {
113
+ pointerEvents: 'none',
114
+ height: isVertical ? size : '100%',
115
+ display: 'none',
116
+ position: 'absolute',
117
+ width: isVertical ? '100%' : size,
118
+ zIndex: 1,
119
+ backgroundImage: (
120
+ `linear-gradient(
121
+ to ${getDirection(direction)},
122
+ rgba(${rgbParam}, 0) 0%,
123
+ rgba(${rgbParam}, 0.9) 51%,
124
+ rgb(${rgbParam}) 75%)`
125
+ )
126
+ })
127
+
128
+ // atributos
129
+ span.setAttribute('id', `scroll-gradient-${direction}-${uuid}`)
130
+ span.setAttribute('role', 'presentation') // remove semântica
131
+ span.setAttribute('tabindex', '-1') // remove do foco
132
+ span.setAttribute('aria-hidden', 'true') // remove do leitor de tela
133
+
134
+ // adiciona span como irmão do elemento acima (top) ou abaixo dele (bottom)
135
+ element.parentNode.insertBefore(span, direction === 'start' ? element.previousSibling : element.nextSibling)
136
+
137
+ // seta posição após inserir o elemento no DOM
138
+ setSpanPosition({ direction, element })
139
+ }
140
+
141
+ /**
142
+ * seta a posição do span de acordo com a posição do elemento.
143
+ *
144
+ * @param {{
145
+ * direction: 'start'|'end',
146
+ * element: HTMLElement
147
+ * }}
148
+ */
149
+ function setSpanPosition ({ direction, element }) {
150
+ const span = getSpan(direction)
151
+
152
+ if (!span) return
153
+
154
+ const elementRect = element.getBoundingClientRect()
155
+ const parentRect = element.parentElement.getBoundingClientRect()
156
+
157
+ /**
158
+ * diferença entre o bottom do pai e o bottom do filho, valor positivo significa
159
+ * que há espaço livre abaixo do filho.
160
+ */
161
+ const distance = {
162
+ end: isVertical ? (parentRect.bottom - elementRect.bottom) : (parentRect.right - elementRect.right),
163
+ start: isVertical ? (elementRect.top - parentRect.top) : (elementRect.left - parentRect.left)
164
+ }
165
+
166
+ span.style[getDirection(direction)] = `${distance[direction]}px`
167
+ }
168
+
169
+ /**
170
+ * controla se o gradient deve ser mostrado ou não de acordo com a posição do scroll.
171
+ *
172
+ * @param {HTMLElement} element
173
+ */
174
+ function toggleSpanByScrollPosition (element) {
175
+ const {
176
+ scrollTop,
177
+ scrollHeight,
178
+ clientHeight,
179
+ scrollLeft,
180
+ scrollWidth,
181
+ clientWidth
182
+ } = element
183
+
184
+ // Pequena tolerância para lidar com problemas de precisão de ponto flutuante
185
+ const tolerance = 2
186
+
187
+ const isScrollAtStart = isVertical ? scrollTop === 0 : scrollLeft === 0
188
+ const isScrollAtEnd = isVertical
189
+ ? scrollTop + clientHeight >= scrollHeight - tolerance
190
+ : scrollLeft + clientWidth >= scrollWidth - tolerance
191
+
192
+ toggleSpan('start', !isScrollAtStart)
193
+ toggleSpan('end', !isScrollAtEnd)
194
+ }
195
+
196
+ function setResizeObserver (element) {
197
+ let previousSize = isVertical ? element.offsetHeight : element.offsetWidth
198
+
199
+ resizeObserver = new ResizeObserver(entries => {
200
+ entries.forEach(entry => {
201
+ // obtém o elemento que foi redimensionado
202
+ const entryElement = entry.target
203
+
204
+ // obtém o novo tamanho
205
+ const currentSize = isVertical ? entryElement.offsetHeight : entryElement.offsetWidth
206
+
207
+ // verifica se o elemento tem scroll no final
208
+ if (!hasEndScroll(entryElement)) {
209
+ toggleSpan('end', false)
210
+
211
+ return
212
+ }
213
+
214
+ // verifica se a altura ou width realmente mudou
215
+ if (previousSize !== currentSize) {
216
+ // atualiza posição do gradient
217
+ setSpanPosition({ direction: 'start', element })
218
+ setSpanPosition({ direction: 'end', element })
219
+
220
+ // controla se deve ou não mostrar o gradient
221
+ toggleSpanByScrollPosition(element)
222
+
223
+ previousSize = currentSize
224
+ }
225
+ })
226
+ })
227
+
228
+ resizeObserver.observe(element)
229
+ }
230
+
231
+ /**
232
+ * verifica se o elemento final (bottom|right) tem scroll.
233
+ *
234
+ * @param {HTMLElement} element
235
+ */
236
+ function hasEndScroll (element) {
237
+ return isVertical
238
+ ? element.scrollHeight > element.clientHeight
239
+ : element.scrollWidth > element.clientWidth
240
+ }
241
+
242
+ /**
243
+ * retorna a direção do gradient de acordo com a orientação do elemento.
244
+ * - se a orientação for vertical, a direção será "top" ou "bottom".
245
+ * - se a orientação for horizontal, a direção será "left" ou "right".
246
+ *
247
+ * @param {Direction} direction
248
+ */
249
+ function getDirection (direction) {
250
+ return isVertical
251
+ ? (direction === 'start' ? 'top' : 'bottom')
252
+ : (direction === 'start' ? 'left' : 'right')
253
+ }
254
+
255
+ return {
256
+ initializeScrollGradient,
257
+ removeScrollGradient,
258
+ uuid,
259
+ resizeObserver
260
+ }
261
+ }
@@ -8,6 +8,7 @@ export default (message, caption) => {
8
8
  caption,
9
9
  classes: 'qas-notification qas-notification--error',
10
10
  message,
11
- type: 'error'
11
+ type: 'error',
12
+ icon: 'sym_r_cancel'
12
13
  })
13
14
  }
@@ -8,6 +8,7 @@ export default (message, caption) => {
8
8
  caption,
9
9
  classes: 'qas-notification qas-notification--success',
10
10
  message,
11
- type: 'success'
11
+ type: 'success',
12
+ icon: 'sym_r_check_circle'
12
13
  })
13
14
  }
package/src/vue-plugin.js CHANGED
@@ -24,6 +24,7 @@ import QasDialog from './components/dialog/QasDialog.vue'
24
24
  import QasDialogRouter from './components/dialog-router/QasDialogRouter.vue'
25
25
  import QasDrawer from './components/drawer/QasDrawer.vue'
26
26
  import QasEmptyResultText from './components/empty-result-text/QasEmptyResultText.vue'
27
+ import QasErrorMessage from './components/error-message/QasErrorMessage.vue'
27
28
  import QasExpansionItem from './components/expansion-item/QasExpansionItem.vue'
28
29
  import QasField from './components/field/QasField.vue'
29
30
  import QasFilters from './components/filters/QasFilters.vue'
@@ -37,7 +38,6 @@ import QasGridItem from './components/grid-item/QasGridItem.vue'
37
38
  import QasHeader from './components/header/QasHeader.vue'
38
39
  import QasInfiniteScroll from './components/infinite-scroll/QasInfiniteScroll.vue'
39
40
  import QasInput from './components/input/QasInput.vue'
40
- import QasInfo from './components/info/QasInfo.vue'
41
41
  import QasLabel from './components/label/QasLabel.vue'
42
42
  import QasLayout from './components/layout/QasLayout.vue'
43
43
  import QasListItems from './components/list-items/QasListItems.vue'
@@ -123,6 +123,7 @@ async function install (app) {
123
123
  app.component('QasDialogRouter', QasDialogRouter)
124
124
  app.component('QasDrawer', QasDrawer)
125
125
  app.component('QasEmptyResultText', QasEmptyResultText)
126
+ app.component('QasErrorMessage', QasErrorMessage)
126
127
  app.component('QasExpansionItem', QasExpansionItem)
127
128
  app.component('QasField', QasField)
128
129
  app.component('QasFilters', QasFilters)
@@ -136,7 +137,6 @@ async function install (app) {
136
137
  app.component('QasHeader', QasHeader)
137
138
  app.component('QasInfiniteScroll', QasInfiniteScroll)
138
139
  app.component('QasInput', QasInput)
139
- app.component('QasInfo', QasInfo)
140
140
  app.component('QasLabel', QasLabel)
141
141
  app.component('QasLayout', QasLayout)
142
142
  app.component('QasListItems', QasListItems)
@@ -224,6 +224,7 @@ export {
224
224
  QasDialogRouter,
225
225
  QasDrawer,
226
226
  QasEmptyResultText,
227
+ QasErrorMessage,
227
228
  QasExpansionItem,
228
229
  QasField,
229
230
  QasFilters,
@@ -237,7 +238,6 @@ export {
237
238
  QasHeader,
238
239
  QasInfiniteScroll,
239
240
  QasInput,
240
- QasInfo,
241
241
  QasLabel,
242
242
  QasLayout,
243
243
  QasListItems,
@@ -1,155 +0,0 @@
1
- <template>
2
- <div class="flex items-center no-wrap qas-info text-body1 text-grey-8">
3
- <qas-avatar v-bind="defaultAvatarProps" />
4
-
5
- <component
6
- :is="textComponent"
7
- v-if="useRegex"
8
- class="q-ml-sm"
9
- />
10
-
11
- <span
12
- v-else
13
- class="q-ml-sm"
14
- >
15
- {{ props.text }}
16
- </span>
17
- </div>
18
- </template>
19
-
20
- <script setup>
21
- import { h, computed } from 'vue'
22
- import { RouterLink } from 'vue-router'
23
- import { QasBtn } from 'asteroid'
24
-
25
- defineOptions({ name: 'QasInfo' })
26
-
27
- const props = defineProps({
28
- avatarProps: {
29
- type: Object,
30
- default: () => ({})
31
- },
32
-
33
- buttonProps: {
34
- type: Object,
35
- default: () => ({})
36
- },
37
-
38
- routerLinkProps: {
39
- type: Object,
40
- default: () => ({})
41
- },
42
-
43
- text: {
44
- type: String,
45
- required: true
46
- },
47
-
48
- useRegex: {
49
- type: Boolean,
50
- default: true
51
- }
52
- })
53
-
54
- const defaultAvatarProps = computed(() => {
55
- return {
56
- color: 'red-14',
57
- ...props.avatarProps,
58
- icon: 'sym_r_priority_high',
59
- size: 'sm'
60
- }
61
- })
62
-
63
- const textComponent = computed(() => {
64
- /**
65
- * regex para encontrar caracteres que estiverem dentro de [].
66
- */
67
- const regex = /\[.*?\]/g
68
-
69
- const [content] = props.text.match(regex)
70
-
71
- /**
72
- * dado o texto: Para saber mais, [Clique aqui].
73
- *
74
- * retorna: 'Clique aqui'
75
- */
76
- const routerLabel = content.replaceAll(/[[\]]/g, '')
77
-
78
- /**
79
- * dado o texto: Para saber mais, [Clique aqui].
80
- *
81
- * retorna: 'Para saber mais, $.'
82
- */
83
- const replacedText = props.text.replaceAll(regex, '$')
84
-
85
- /**
86
- * É necessário usar o regex ao invés de '$' para ele não remover o carácter
87
- * ao fazer o split
88
- *
89
- * dado o texto: 'Para saber mais, [Clique aqui].'
90
- *
91
- * retorna: ['Para saber mais,', '$', '.']
92
- *
93
- */
94
- const splitted = replacedText.split(/(\$)/)
95
-
96
- const index = splitted.findIndex(item => item === '$')
97
-
98
- const hasButtonProps = !!Object.keys(props.buttonProps).length
99
-
100
- const getRouterLinkRender = () => {
101
- return h(
102
- RouterLink,
103
- {
104
- ...props.routerLinkProps,
105
- class: 'text-primary text-subtitle1 qas-info__link'
106
- },
107
- {
108
- default: () => routerLabel
109
- }
110
- )
111
- }
112
-
113
- const getQasBtnRender = () => {
114
- return h(
115
- QasBtn,
116
- {
117
- variant: 'tertiary',
118
- label: routerLabel,
119
- ...props.buttonProps
120
- }
121
- )
122
- }
123
-
124
- /**
125
- * Cria um render do router link ou QasBtn
126
- *
127
- * @example
128
- *
129
- * ```html
130
- * <router-link
131
- * class="text-primary text-subtitle1 qas-info__link"
132
- * :to="props.route"
133
- * >
134
- * Clique aqui
135
- * </router-link>
136
- * ```
137
- */
138
- const renderComponent = hasButtonProps ? getQasBtnRender() : getRouterLinkRender()
139
-
140
- splitted.splice(index, 1, renderComponent)
141
-
142
- return h(
143
- 'span',
144
- splitted
145
- )
146
- })
147
- </script>
148
-
149
- <style lang="scss">
150
- .qas-info {
151
- &__link {
152
- text-decoration: none;
153
- }
154
- }
155
- </style>
@@ -1,34 +0,0 @@
1
- type: component
2
-
3
- meta:
4
- desc: Componente para informações.
5
-
6
- props:
7
- avatar-props:
8
- desc: Props do componente QasAvatar.
9
- default:
10
- color: 'red-14'
11
- icon: 'sym_r_priority_high'
12
- size: 'sm'
13
- type: Object
14
-
15
- buttonProps:
16
- desc: Props do componente QasBtn.
17
- default:
18
- variant: primary
19
- type: Object
20
-
21
- router-link-props:
22
- desc: Propriedades do componente RouterLink.
23
- default: {}
24
- type: Object
25
-
26
- text:
27
- desc: Texto a ser exibido.
28
- type: String
29
- default: ''
30
-
31
- use-regex:
32
- desc: Faz a busca pelos "[]" dentro da propriedade "text" para substituir pelo QasBtn ou RouterLink.
33
- default: true
34
- type: Boolean
@@ -1,14 +0,0 @@
1
- .q-checkbox {
2
- &__label {
3
- @include set-typography($body1);
4
-
5
- padding-left: var(--qas-spacing-sm) !important;
6
- }
7
-
8
- &.disabled {
9
- .q-checkbox__label,
10
- .q-checkbox__inner {
11
- color: $grey-6;
12
- }
13
- }
14
- }
@@ -1,18 +0,0 @@
1
- .q-radio {
2
- &__label {
3
- @include set-typography($body2);
4
-
5
- padding-left: var(--qas-spacing-sm) !important;
6
- }
7
-
8
- &__inner::before {
9
- color: $primary;
10
- }
11
-
12
- &.disabled {
13
- .q-radio__label,
14
- .q-radio__inner {
15
- color: $grey-6;
16
- }
17
- }
18
- }
@@ -1,13 +0,0 @@
1
- .q-toggle {
2
- &__label {
3
- @include set-typography($body2);
4
-
5
- padding-left: var(--qas-spacing-sm) !important;
6
- }
7
-
8
- &.disabled {
9
- .q-toggle__label {
10
- color: $grey-6;
11
- }
12
- }
13
- }