@bildvitta/quasar-ui-asteroid 3.20.0-beta.18 → 3.20.0-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bildvitta/quasar-ui-asteroid",
3
3
  "description": "Asteroid",
4
- "version": "3.20.0-beta.18",
4
+ "version": "3.20.0-beta.19",
5
5
  "author": "Bild & Vitta <systemteam@bild.com.br>",
6
6
  "license": "MIT",
7
7
  "main": "./src/asteroid.js",
@@ -388,8 +388,6 @@ const hasColumnsLength = computed(() => !!Object.keys(columnsResultsModel.value)
388
388
 
389
389
  const containerStyle = computed(() => `width: ${props.columnWidth};`)
390
390
 
391
- const hasConfirmDialogProps = computed(() => !!Object.keys(props.confirmDialogProps).length)
392
-
393
391
  const defaultConfirmDialogProps = computed(() => {
394
392
  const defaultProps = {
395
393
  ok: {
@@ -1124,7 +1122,7 @@ function onDropCard (event) {
1124
1122
  return
1125
1123
  }
1126
1124
 
1127
- hasConfirmDialogProps.value
1125
+ props.useConfirmDialog
1128
1126
  ? openConfirmDialog()
1129
1127
  : confirmDrop(event)
1130
1128
  }
@@ -1143,7 +1141,7 @@ function closeConfirmDialog () {
1143
1141
  function cancelDrop (event) {
1144
1142
  revertDomDrag(event)
1145
1143
 
1146
- if (hasConfirmDialogProps.value) closeConfirmDialog()
1144
+ if (props.useConfirmDialog) closeConfirmDialog()
1147
1145
 
1148
1146
  stopDragging()
1149
1147
  }
@@ -115,6 +115,10 @@ props:
115
115
  default: "{ animation: 500, swapThreshold: 1, delay: 50, delayOnTouchOnly: true, emptyInsertThreshold: 0 }"
116
116
  debugger: true
117
117
 
118
+ use-confirm-dialog:
119
+ desc: Controla se o dialog de confirmação para o drag and drop será utilizado. Caso seja "true", o dialog é aberto utilizando as props passadas em "confirm-dialog-props" e as ações de confirmar e cancelar são feitas por padrão no componente. Caso seja "false", o drag and drop é realizado sem confirmação.
120
+ type: Boolean
121
+
118
122
  use-drag-and-drop-x:
119
123
  desc: Controla se irá ter drag and drop no eixo X (entre colunas).
120
124
  type: Boolean
@@ -1,37 +1,29 @@
1
1
  <template>
2
- <q-dialog ref="dialogRef" class="qas-dialog" :class="classes" data-cy="dialog" v-bind="dialogProps" :persistent="props.persistent" @update:model-value="updateModelValue">
3
- <div class="bg-white q-pa-md" :style="style">
4
- <header v-if="hasHeader" class="q-mb-md">
5
- <slot name="header">
6
- <div class="items-center justify-between row">
7
- <qas-label data-cy="dialog-title" :label="props.card.title" margin="none">
8
- <slot name="title" />
9
- </qas-label>
10
-
11
- <qas-btn v-if="isInfoDialog" v-close-popup color="grey-10" data-cy="dialog-close-btn" icon="sym_r_close" variant="tertiary" />
12
- </div>
13
- </slot>
2
+ <q-dialog ref="dialogRef" v-model="model" class="qas-dialog" :class="classes" data-cy="dialog" v-bind="dialogProps">
3
+ <div class="bg-white full-width q-pa-md qas-dialog__container">
4
+ <header v-if="hasHeaderSlot" class="q-mb-md">
5
+ <slot name="header" />
14
6
  </header>
15
7
 
8
+ <qas-header v-else-if="props.title" v-bind="headerProps">
9
+ <template v-if="hasTitleSlot" #label>
10
+ <qas-label>
11
+ <slot name="title" />
12
+ </qas-label>
13
+ </template>
14
+ </qas-header>
15
+
16
16
  <section class="relative-position text-body1 text-grey-8">
17
17
  <component :is="mainComponent.is" ref="form" v-bind="mainComponent.props">
18
18
  <slot name="description">
19
- <component :is="descriptionComponent" data-cy="dialog-description">{{ props.card.description }}</component>
19
+ <div v-if="props.useHtmlDescription" data-cy="dialog-description" v-html="props.description" />
20
+
21
+ <component :is="descriptionComponent" v-else data-cy="dialog-description">
22
+ {{ props.description }}
23
+ </component>
20
24
  </slot>
21
25
 
22
- <div v-if="!isInfoDialog">
23
- <slot name="actions">
24
- <qas-actions v-bind="defaultActionsProps">
25
- <template v-if="hasOk" #primary>
26
- <qas-btn v-close-popup="!props.useForm" class="full-width" data-cy="dialog-ok-btn" size="lg" variant="primary" v-bind="defaultOk" />
27
- </template>
28
-
29
- <template v-if="hasCancel" #secondary>
30
- <qas-btn v-close-popup class="full-width" data-cy="dialog-cancel-btn" size="lg" v-bind="defaultCancel" variant="secondary" />
31
- </template>
32
- </qas-actions>
33
- </slot>
34
- </div>
26
+ <qas-actions v-if="hasActions" class="qas-dialog__actions" v-bind="defaultActionsProps" />
35
27
  </component>
36
28
  </section>
37
29
  </div>
@@ -39,24 +31,19 @@
39
31
  </template>
40
32
 
41
33
  <script setup>
42
- import QasActions from '../actions/QasActions.vue'
43
- import QasBtn from '../btn/QasBtn.vue'
44
34
  import QasLabel from '../label/QasLabel.vue'
35
+ import QasActions from '../actions/QasActions.vue'
36
+ import QasHeader from '../header/QasHeader.vue'
45
37
 
46
- import useCancel from './composables/use-cancel'
47
- import useDynamicComponents from './composables/use-dynamic-components'
48
- import useOk from './composables/use-ok'
49
- import { useScreen } from '../../composables'
50
-
51
- import { computed, ref, useAttrs, useSlots, provide } from 'vue'
52
- import { useDialogPluginComponent } from 'quasar'
38
+ import { computed, ref, useAttrs, provide, useSlots, inject } from 'vue'
39
+ import { useDialogPluginComponent, QForm } from 'quasar'
53
40
 
54
- defineOptions({ name: 'QasDialog' })
41
+ defineOptions({ name: 'QasDialog', inheritAttrs: false })
55
42
 
56
43
  const props = defineProps({
57
- actionsProps: {
58
- default: () => ({}),
59
- type: Object
44
+ badges: {
45
+ default: () => [],
46
+ type: [Array, Object]
60
47
  },
61
48
 
62
49
  cancel: {
@@ -64,19 +51,13 @@ const props = defineProps({
64
51
  type: [Object, Boolean]
65
52
  },
66
53
 
67
- card: {
68
- default: () => ({}),
69
- type: Object
70
- },
71
-
72
- maxWidth: {
73
- default: '',
74
- type: String
54
+ description: {
55
+ type: [String, Object],
56
+ default: ''
75
57
  },
76
58
 
77
- minWidth: {
78
- default: '',
79
- type: String
59
+ disableCloseButton: {
60
+ type: Boolean
80
61
  },
81
62
 
82
63
  ok: {
@@ -84,16 +65,23 @@ const props = defineProps({
84
65
  type: [Object, Boolean]
85
66
  },
86
67
 
87
- persistent: {
88
- default: true,
89
- type: Boolean
68
+ size: {
69
+ type: String,
70
+ default: 'sm',
71
+ validator: value => ['sm', 'md', 'lg', 'xl'].includes(value)
90
72
  },
91
73
 
92
- useForm: {
93
- type: Boolean
74
+ title: {
75
+ type: String,
76
+ required: true
94
77
  },
95
78
 
96
- modelValue: {
79
+ tertiary: {
80
+ default: () => ({}),
81
+ type: Object
82
+ },
83
+
84
+ useForm: {
97
85
  type: Boolean
98
86
  },
99
87
 
@@ -101,7 +89,22 @@ const props = defineProps({
101
89
  type: Boolean
102
90
  },
103
91
 
104
- useFullMaxWidth: {
92
+ useAutoCloseOnCancel: {
93
+ type: Boolean,
94
+ default: true
95
+ },
96
+
97
+ useAutoCloseOnOk: {
98
+ type: Boolean,
99
+ default: true
100
+ },
101
+
102
+ useCloseButton: {
103
+ type: Boolean,
104
+ default: true
105
+ },
106
+
107
+ useHtmlDescription: {
105
108
  type: Boolean
106
109
  },
107
110
 
@@ -110,10 +113,8 @@ const props = defineProps({
110
113
  }
111
114
  })
112
115
 
116
+ // emtis
113
117
  const emit = defineEmits([
114
- // model
115
- 'update:modelValue',
116
-
117
118
  // actions
118
119
  'cancel',
119
120
  'ok',
@@ -123,24 +124,28 @@ const emit = defineEmits([
123
124
  ...useDialogPluginComponent.emits
124
125
  ])
125
126
 
126
- provide('isDialog', true)
127
+ // models
128
+ const model = defineModel({ type: Boolean })
127
129
 
128
- const attrs = useAttrs()
129
- const screen = useScreen()
130
- const slots = useSlots()
130
+ // globals
131
+ const drawerProps = inject('drawerProps', null)
131
132
 
132
- // usado para o plugin
133
- const { dialogRef, onDialogHide } = useDialogPluginComponent()
133
+ provide('isDialog', true)
134
+ provide('btnPropsDefaults', { size: 'md' }) // define o tamanho padrão para os botões dentro do dialog
135
+ provide('drawerProps', null) // quebra a cadeia para dialogs filhos usarem hasActions normalmente
134
136
 
135
- // QForm template
137
+ // refs
136
138
  const form = ref(null)
137
139
 
138
- const composablesParams = { emit, form, props, screen, slots }
139
-
140
- const { defaultCancel, hasCancel } = useCancel(composablesParams)
141
- const { defaultOk, hasOk, onOk } = useOk(composablesParams)
142
- const { descriptionComponent, mainComponent } = useDynamicComponents({ ...composablesParams, onOk, hasOk })
140
+ // composables
141
+ const slots = useSlots()
142
+ const attrs = useAttrs()
143
+ const { dialogRef, onDialogHide } = useDialogPluginComponent() // usado para o plugin
144
+ const { defaultCancel, hasCancel } = useCancel()
145
+ const { defaultOk, hasOk, onOk } = useOk()
146
+ const { descriptionComponent, mainComponent } = useDynamicComponents()
143
147
 
148
+ // computeds
144
149
  /**
145
150
  * Classes criadas para serem utilizadas quando usado com a prop "position", pois
146
151
  * o comportamento do dialog muda, e não é possível usar em conjunto com a prop
@@ -150,60 +155,236 @@ const classes = computed(() => {
150
155
  const isRightPosition = attrs.position === 'right'
151
156
  const isLeftPosition = attrs.position === 'left'
152
157
 
153
- return {
154
- 'qas-dialog--right': isRightPosition,
155
- 'qas-dialog--left': isLeftPosition
158
+ const sizes = {
159
+ sm: 'qas-dialog--sm', // 450px
160
+ md: 'qas-dialog--md', // 550px
161
+ lg: 'qas-dialog--lg', // 800px
162
+ xl: 'qas-dialog--xl' // 1100px
156
163
  }
164
+
165
+ return [
166
+ sizes[props.size],
167
+
168
+ {
169
+ 'qas-dialog--right': isRightPosition,
170
+ 'qas-dialog--left': isLeftPosition
171
+ }
172
+ ]
157
173
  })
158
174
 
159
175
  const dialogProps = computed(() => {
176
+ const { title, ...attributes } = attrs
177
+
160
178
  return {
161
179
  ...(!props.usePlugin && { modelValue: props.modelValue }),
162
- ...attrs,
180
+ ...attributes,
181
+
182
+ /**
183
+ * valida se a prop "persistent" foi passada para o drawer, caso tenha passado,
184
+ * ele respeita a prop, caso contrário é validado se tem ações para ser persistente ou não.
185
+ */
186
+ persistent: drawerProps?.value ? drawerProps?.value.persistent : hasActions.value,
163
187
 
164
188
  onHide: onDialogHide
165
189
  }
166
190
  })
167
191
 
168
- const style = computed(() => {
192
+ const hasActions = computed(() => hasOk.value || hasCancel.value || !!Object.keys(props.tertiary).length)
193
+
194
+ const headerProps = computed(() => {
169
195
  return {
170
- ...(props.useFullMaxWidth && { width: '100%' }),
196
+ labelProps: {
197
+ label: props.title
198
+ },
199
+
200
+ badges: props.badges,
201
+
202
+ buttonProps: {
203
+ ...(props.useCloseButton && {
204
+ color: 'grey-10',
205
+ disable: props.disableCloseButton,
206
+ icon: 'sym_r_close',
207
+ variant: 'tertiary',
208
+ 'data-cy': 'dialog-close-btn',
209
+ onClick: () => updateModelValue(false)
210
+ })
211
+ }
212
+ }
213
+ })
171
214
 
172
- maxWidth: props.maxWidth || '470px',
173
- minWidth: props.minWidth || (screen.isSmall ? '' : '366px')
215
+ const defaultActionsProps = computed(() => {
216
+ return {
217
+ ...(hasOk.value && { primaryButtonProps: defaultOk.value }),
218
+ ...(hasCancel.value && { secondaryButtonProps: defaultCancel.value }),
219
+
220
+ tertiaryButtonProps: {
221
+ ...props.tertiary,
222
+ 'data-cy': 'dialog-tertiary-btn'
223
+ },
224
+
225
+ spacingTop: 'lg',
226
+ gutter: 'md'
174
227
  }
175
228
  })
176
229
 
177
- const hasHeader = computed(() => !!slots.header || props.card.title)
178
- const isInfoDialog = computed(() => !hasOk.value && !hasCancel.value)
230
+ const hasHeaderSlot = computed(() => !!slots.header)
231
+ const hasTitleSlot = computed(() => !!slots.title)
179
232
 
180
- const defaultActionsProps = computed(() => {
181
- const { useFullWidth, useEqualWidth } = props.actionsProps
233
+ // functions
234
+ function updateModelValue (value) {
235
+ model.value = value
236
+ }
182
237
 
183
- if (useFullWidth || useEqualWidth) return props.actionsProps
238
+ // composable definitions
239
+ function useOk () {
240
+ // computeds
241
+ const defaultOk = computed(() => {
242
+ const { onClick, ...attrs } = props.ok
243
+
244
+ return {
245
+ label: 'Ok',
246
+ type: (props.ok?.type || props.useForm) ? 'submit' : 'button',
247
+ 'data-cy': 'dialog-ok-btn',
248
+
249
+ ...attrs,
250
+
251
+ // adiciona somente se não estiver usando useForm pois o controle ficará no submit.
252
+ ...(!props.useForm && { onClick: onOk })
253
+ }
254
+ })
255
+
256
+ const hasOk = computed(() => typeof props.ok === 'boolean' ? props.ok : !!Object.keys(props.ok)?.length)
257
+
258
+ // functions
259
+ function onOk () {
260
+ if (!props.useForm && props.useAutoCloseOnOk) {
261
+ updateModelValue(false)
262
+ }
184
263
 
185
- const hasAllActions = hasOk.value && hasCancel.value
186
- const hasSingleAction = (hasOk.value && !hasCancel.value) || (!hasOk.value && hasCancel.value)
264
+ props.ok.onClick?.()
265
+
266
+ emit('ok')
267
+ }
187
268
 
188
269
  return {
189
- useFullWidth: hasSingleAction,
190
- useEqualWidth: hasAllActions,
270
+ defaultOk,
271
+ hasOk,
272
+ onOk
273
+ }
274
+ }
275
+
276
+ function useCancel () {
277
+ // computeds
278
+ const defaultCancel = computed(() => {
279
+ return {
280
+ label: 'Cancelar',
281
+ 'data-cy': 'dialog-cancel-btn',
282
+
283
+ ...props.cancel,
284
+
285
+ onClick: onCancel
286
+ }
287
+ })
191
288
 
192
- ...props.actionsProps
289
+ const hasCancel = computed(() => typeof props.cancel === 'boolean' ? props.cancel : !!Object.keys(props.cancel)?.length)
290
+
291
+ // functions
292
+ function onCancel () {
293
+ props.cancel.onClick?.()
294
+
295
+ if (props.useAutoCloseOnAction || props.useAutoCloseOnCancel) {
296
+ updateModelValue(false)
297
+ }
298
+
299
+ emit('cancel')
193
300
  }
194
- })
195
301
 
196
- function updateModelValue (value) {
197
- emit('update:modelValue', value)
302
+ return {
303
+ defaultCancel,
304
+ hasCancel
305
+ }
306
+ }
307
+
308
+ function useDynamicComponents () {
309
+ // computeds
310
+ const mainComponent = computed(() => {
311
+ return {
312
+ is: props.useForm ? QForm : 'div',
313
+
314
+ /**
315
+ * adiciona evento de submit caso useForm seja true,
316
+ * uma vez que somente o q-form possui este evento.
317
+ */
318
+ props: {
319
+ ...(props.useForm && { onSubmit })
320
+ }
321
+ }
322
+ })
323
+
324
+ const hasRenderFunction = computed(() => {
325
+ const description = props.description
326
+
327
+ return typeof description === 'object' && description !== null && !Array.isArray(description)
328
+ })
329
+
330
+ const descriptionComponent = computed(() => hasRenderFunction.value ? props.description : 'div')
331
+
332
+ // functions
333
+ function submitHandler () {
334
+ if (!props.useForm) return
335
+
336
+ if (props.useValidationAllAtOnce) {
337
+ let isAllComponentValid = true
338
+ const components = form.value.getValidationComponents() || []
339
+
340
+ for (const component of components) {
341
+ const isValid = component?.validate?.()
342
+
343
+ if (!isValid) {
344
+ isAllComponentValid = false
345
+ }
346
+ }
347
+
348
+ emit('validate', isAllComponentValid)
349
+
350
+ return
351
+ }
352
+
353
+ emit('validate', form.value.validate())
354
+ }
355
+
356
+ /**
357
+ * Sem este método, ao clicar enter com a prop useForm ativada a tela era recarregada,
358
+ * e a ação de click do botão não era chamada pois ele não esta dentro do form.
359
+ */
360
+ function onSubmit (event) {
361
+ event.preventDefault()
362
+
363
+ if (hasOk.value) {
364
+ onOk()
365
+ submitHandler()
366
+ }
367
+ }
368
+
369
+ return {
370
+ mainComponent,
371
+ descriptionComponent
372
+ }
198
373
  }
199
374
  </script>
200
375
 
201
376
  <style lang="scss">
202
377
  .qas-dialog {
378
+ $root: &;
379
+
203
380
  .q-dialog__inner > div {
204
381
  box-shadow: $shadow-2;
205
382
  }
206
383
 
384
+ .q-dialog__inner--minimized {
385
+ padding: var(--qas-spacing-md);
386
+ }
387
+
207
388
  &--right {
208
389
  .q-dialog__inner {
209
390
  width: 100%;
@@ -216,5 +397,39 @@ function updateModelValue (value) {
216
397
  width: 100%;
217
398
  }
218
399
  }
400
+
401
+ // tamanhos
402
+ &--sm {
403
+ #{$root}__container {
404
+ max-width: 450px !important;
405
+ }
406
+ }
407
+
408
+ &--md {
409
+ #{$root}__container {
410
+ max-width: 550px !important;
411
+ }
412
+ }
413
+
414
+ &--lg {
415
+ #{$root}__container {
416
+ max-width: 800px !important;
417
+ }
418
+ }
419
+
420
+ &--xl {
421
+ #{$root}__container {
422
+ max-width: 1100px !important;
423
+ }
424
+ }
425
+
426
+ // tamanho mínimo dos botões de ação (primário e secundário)
427
+ &__actions {
428
+ .qas-btn--primary,
429
+ .qas-btn--secondary {
430
+ min-width: 120px;
431
+ width: 100%;
432
+ }
433
+ }
219
434
  }
220
435
  </style>
@@ -4,10 +4,10 @@ meta:
4
4
  desc: Componente de dialog.
5
5
 
6
6
  props:
7
- actions-props:
8
- desc: Props repassadas para o "QasActions".
9
- default: {}
10
- type: Object
7
+ badges:
8
+ desc: Badges que aparecem no canto superior direito do dialog.
9
+ type: [Array, Object]
10
+ default: []
11
11
 
12
12
  cancel:
13
13
  desc: Props para o botão de cancelar, pode ser objeto com as propriedades ou um boolean, caso for "false", remove o botão de cancelar.
@@ -15,19 +15,13 @@ props:
15
15
  type: [Object, Boolean]
16
16
  examples: ["{ label: 'Meu botão de cancelar', onClick: () => alert('fui clicado!') }"]
17
17
 
18
- card:
19
- desc: Objeto contendo as informações para serem exibidas dentro do dialog como "title" e "description".
20
- default: {}
21
- type: Object
22
- examples: ["{ title: 'Meu título', description: 'Minha descrição' }"]
23
-
24
- max-width:
25
- desc: Tamanho máximo do dialog.
26
- type: String
18
+ description:
19
+ desc: Descrição do dialog, pode ser uma string, um componente ou uma string com HTML (passando a prop useHtmlDescription).
20
+ type: [String, Object]
27
21
 
28
- min-width:
29
- desc: Tamanho mínimo do dialog.
30
- type: String
22
+ disable-close-button:
23
+ desc: Define se o botão de fechar no canto superior direito estará desabilitado.
24
+ type: Boolean
31
25
 
32
26
  model-value:
33
27
  desc: Model do componente, abre ou fecha o dialog.
@@ -42,27 +36,49 @@ props:
42
36
  type: [Object, Boolean]
43
37
  examples: ["{ label: 'Meu botão de confirmar', onClick: () => alert('fui clicado!') }"]
44
38
 
45
- persistent:
46
- desc: Define se o dialog vai fechar ou não após clicar fora do dialog.
39
+ size:
40
+ desc: Tamanho do dialog.
41
+ type: String
42
+ default: 'md'
43
+ options: ['xs', 'sm', 'md', 'lg', 'xl']
44
+
45
+ use-auto-close-on-cancel:
46
+ desc: Define se o dialog vai fechar automaticamente ao clicar no botão de cancelar.
47
+ type: Boolean
48
+ default: true
49
+
50
+ use-auto-close-on-ok:
51
+ desc: Define se o dialog vai fechar automaticamente ao clicar no botão de confirmar (ok).
52
+ type: Boolean
47
53
  default: true
54
+
55
+ use-close-button:
56
+ desc: Define se o dialog vai ter um botão de fechar no canto superior direito.
48
57
  type: Boolean
49
58
 
50
59
  use-form:
51
60
  desc: Define se a tag onde fica a descrição no dialog vai ser um "<q-form />" ou "<div />".
52
61
  type: Boolean
53
62
 
54
- use-full-max-width:
55
- desc: propriedade para utilizar `100% do maxWidth`.
63
+ use-html-description:
64
+ desc: Define se a descrição aceita HTML (utilize com cuidado para evitar XSS).
56
65
  type: Boolean
57
66
 
58
67
  use-validation-all-at-once:
59
68
  desc: Valida todos os campos de uma única vez, ao invés de ser um por vez (que é o padrão).
60
69
  type: Boolean
61
70
 
62
- slots:
63
- actions:
64
- desc: Slot para ações (botões por exemplo).
71
+ tertiary:
72
+ desc: Props para o botão terciário
73
+ type: Object
74
+ default: {}
75
+
76
+ title:
77
+ desc: Título do dialog.
78
+ type: String
79
+ required: true
65
80
 
81
+ slots:
66
82
  description:
67
83
  desc: Slot para descrição.
68
84
 
@@ -104,6 +120,10 @@ selectors:
104
120
  desc: Seletor do botão de cancelar do componente.
105
121
  examples: ['data-cy="dialog-cancel-btn"']
106
122
 
123
+ dialog-tertiary-btn:
124
+ desc: Seletor do botão terciário do componente.
125
+ examples: ['data-cy="dialog-tertiary-btn"']
126
+
107
127
  dialog-close-btn:
108
128
  desc: Seletor do botão de fechar do componente.
109
129
  examples: ['data-cy="dialog-close-btn"']
@@ -15,7 +15,7 @@ export default function useCancel (config = {}) {
15
15
  const defaultCancel = computed(() => {
16
16
  return {
17
17
  label: 'Cancelar',
18
- outline: true,
18
+ 'data-cy': 'dialog-cancel-btn',
19
19
 
20
20
  ...props.cancel,
21
21
 
@@ -35,12 +35,12 @@ export default function useDynamicComponents (config = {}) {
35
35
  })
36
36
 
37
37
  const hasRenderFunction = computed(() => {
38
- const description = props.card.description
38
+ const description = props.description
39
39
 
40
40
  return typeof description === 'object' && description !== null && !Array.isArray(description)
41
41
  })
42
42
 
43
- const descriptionComponent = computed(() => hasRenderFunction.value ? props.card.description : 'div')
43
+ const descriptionComponent = computed(() => hasRenderFunction.value ? props.description : 'div')
44
44
 
45
45
  // métodos
46
46
  function submitHandler () {
@@ -18,6 +18,7 @@ export default function useOk (config = {}) {
18
18
  return {
19
19
  label: 'Ok',
20
20
  type: (props.ok?.type || props.useForm) ? 'submit' : 'button',
21
+ 'data-cy': 'dialog-ok-btn',
21
22
 
22
23
  ...attrs,
23
24
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <qas-dialog class="qas-drawer" v-bind="attributes" @update:model-value="onUpdateModelValue">
2
+ <qas-dialog v-model="model" class="qas-drawer" :class="containerDialogClasses" v-bind="attributes">
3
3
  <template #header>
4
4
  <slot name="header">
5
5
  <div class="items-center justify-between row">
@@ -11,18 +11,18 @@
11
11
  </slot>
12
12
  </span>
13
13
 
14
- <qas-btn v-close-popup class="z-max" color="grey-10" data-cy="drawer-close-btn" icon="sym_r_close" variant="tertiary" @click="emit('update:modelValue', false)" />
14
+ <qas-btn class="z-max" color="grey-10" data-cy="drawer-close-btn" icon="sym_r_close" variant="tertiary" @click="close" />
15
15
  </div>
16
16
  </slot>
17
17
  </template>
18
18
 
19
19
  <template #description>
20
- <div>
21
- <div class="relative-position" data-cy="drawer-default">
20
+ <div class="relative-position">
21
+ <div data-cy="drawer-default">
22
22
  <slot />
23
23
  </div>
24
24
 
25
- <div v-if="props.loading" class="qas-drawer__loading" :style="loadingStyle">
25
+ <div v-if="props.loading" class="qas-drawer__loading">
26
26
  <div class="full-height relative-position">
27
27
  <q-inner-loading :showing="props.loading">
28
28
  <q-spinner color="grey" size="2em" />
@@ -40,7 +40,7 @@ import QasBtn from '../btn/QasBtn.vue'
40
40
 
41
41
  import useScreen from '../../composables/use-screen.js'
42
42
 
43
- import { computed, useAttrs } from 'vue'
43
+ import { computed, inject, provide } from 'vue'
44
44
 
45
45
  defineOptions({
46
46
  name: 'QasDrawer',
@@ -53,9 +53,8 @@ const props = defineProps({
53
53
  default: () => ({})
54
54
  },
55
55
 
56
- maxWidth: {
57
- type: String,
58
- default: '60%'
56
+ persistent: {
57
+ type: Boolean
59
58
  },
60
59
 
61
60
  position: {
@@ -71,43 +70,57 @@ const props = defineProps({
71
70
 
72
71
  loading: {
73
72
  type: Boolean
73
+ },
74
+
75
+ size: {
76
+ type: String,
77
+ default: 'sm',
78
+ validator: value => !value || ['sm', 'md', 'lg', 'xl'].includes(value)
74
79
  }
75
80
  })
76
81
 
77
- const emit = defineEmits(['update:modelValue'])
82
+ // globals
83
+ const isOverlay = inject('isOverlay', false)
84
+
85
+ provide('drawerProps', computed(() => ({ persistent: props.persistent })))
78
86
 
79
- const attrs = useAttrs()
87
+ // emits
88
+ const model = defineModel({ type: Boolean })
89
+
90
+ // composables
80
91
  const screen = useScreen()
81
92
 
82
93
  // computed
83
- const normalizedMaxWidth = computed(() => screen.isSmall ? '95%' : props.maxWidth)
84
-
85
- const loadingStyle = computed(() => {
86
- return {
87
- right: `calc(100% - ${normalizedMaxWidth.value})`
88
- }
94
+ const containerDialogClasses = computed(() => {
95
+ // tratamento para mobile, onde sempre pegará 95% da tela.
96
+ if (screen.isSmall) return 'qas-drawer--mobile'
97
+
98
+ /**
99
+ * no caso de ter passado a prop lg ou xl, mas o tamanho da tela ser médio,
100
+ * o drawer irá se comportar como md, pegando 512px de largura, caso contrário, teria o problema
101
+ * do drawer acabar pegando 100% da página, tendo o comportamento errado.
102
+ */
103
+ if (screen.isMedium && (props.size === 'lg' || props.size === 'xl')) return 'qas-drawer--md'
104
+
105
+ return isOverlay ? 'qas-drawer--overlay' : `qas-drawer--${props.size}`
89
106
  })
90
107
 
91
108
  const attributes = computed(() => {
92
- const { modelValue } = attrs
93
-
94
109
  return {
95
- persistent: false,
96
- modelValue,
97
-
98
110
  ...props.dialogProps,
99
111
 
112
+ title: props.title,
100
113
  cancel: false,
101
- maxWidth: normalizedMaxWidth.value,
102
114
  maximized: true,
103
115
  ok: false,
104
116
  position: props.position,
105
- useFullMaxWidth: true
117
+ persistent: props.persistent
106
118
  }
107
119
  })
108
120
 
109
- function onUpdateModelValue (value) {
110
- emit('update:modelValue', value)
121
+ // functions
122
+ function close () {
123
+ model.value = false
111
124
  }
112
125
  </script>
113
126
 
@@ -118,6 +131,43 @@ function onUpdateModelValue (value) {
118
131
  left: 0;
119
132
  position: absolute;
120
133
  top: 0;
134
+ width: 100%;
135
+ }
136
+
137
+ &--mobile {
138
+ .qas-dialog__container {
139
+ max-width: 95% !important;
140
+ }
141
+ }
142
+
143
+ &--overlay {
144
+ .qas-dialog__container {
145
+ max-width: 90% !important;
146
+ }
147
+ }
148
+
149
+ &--sm {
150
+ .qas-dialog__container {
151
+ max-width: 368px !important;
152
+ }
153
+ }
154
+
155
+ &--md {
156
+ .qas-dialog__container {
157
+ max-width: 512px !important;
158
+ }
159
+ }
160
+
161
+ &--lg {
162
+ .qas-dialog__container {
163
+ max-width: 656px !important;
164
+ }
165
+ }
166
+
167
+ &--xl {
168
+ .qas-dialog__container {
169
+ max-width: 960px !important;
170
+ }
121
171
  }
122
172
  }
123
173
  </style>
@@ -20,6 +20,11 @@ props:
20
20
  examples: [v-model="value"]
21
21
  model: true
22
22
 
23
+ persistent:
24
+ desc: Define se o drawer pode ser fechado clicando fora dele ou pressionando a tecla ESC.
25
+ default: false
26
+ type: Boolean
27
+
23
28
  position:
24
29
  desc: 'Posição do drawer, sendo possível apenas 2 opções: [left, right].'
25
30
  default: left
@@ -29,6 +34,11 @@ props:
29
34
  desc: Titulo do drawer.
30
35
  type: String
31
36
 
37
+ size:
38
+ desc: Tamanho do drawer.
39
+ default: sm
40
+ type: String
41
+
32
42
  slots:
33
43
  header:
34
44
  desc: Slot para conteúdo do cabeçalho.
@@ -190,9 +190,9 @@ export default {
190
190
  ignoreRouterGuard: false,
191
191
 
192
192
  defaultDialogProps: {
193
- card: {
194
- description: 'Você está deixando a página e suas alterações serão perdidas. Tem certeza que deseja sair sem salvar?'
195
- },
193
+ title: 'Alterações não salvas',
194
+ description: 'Você está deixando a página e suas alterações serão perdidas. Tem certeza que deseja sair sem salvar?',
195
+ size: 'md',
196
196
 
197
197
  ok: { label: 'Continuar editando' },
198
198
 
@@ -28,9 +28,8 @@ export default function useDelete ({ props, destroyFn, emit }) {
28
28
 
29
29
  const defaultDialogProps = computed(() => {
30
30
  return {
31
- card: {
32
- description: 'Tem certeza que deseja excluir este item?'
33
- },
31
+ title: 'Excluir',
32
+ description: 'Tem certeza que deseja excluir este item?',
34
33
 
35
34
  ok: {
36
35
  label: 'Excluir',
@@ -1,12 +1,6 @@
1
1
  <template>
2
2
  <div class="pv-gallery-carousel-dialog">
3
- <qas-dialog v-model="model" :cancel="false" class="q-pa-xl" max-width="1100px" :ok="false" :persistent="false" use-full-max-width>
4
- <template #header>
5
- <div class="text-right">
6
- <qas-btn v-close-popup color="grey-10" icon="sym_r_close" variant="tertiary" @click="close" />
7
- </div>
8
- </template>
9
-
3
+ <qas-dialog v-model="model" v-bind="dialogProps">
10
4
  <template #description>
11
5
  <q-carousel v-model="imageIndexModel" animated :arrows="!screen.isSmall" class="pv-gallery-carousel-dialog__carousel" control-text-color="primary" data-cy="gallery-carousel" :fullscreen="screen.isSmall" :height="carouselImageHeight" next-icon="sym_r_chevron_right" prev-icon="sym_r_chevron_left" swipeable :thumbnails="!isSingleImage">
12
6
  <q-carousel-slide v-for="(image, index) in props.images" :key="index" class="bg-no-repeat bg-size-contain" :data-cy="`gallery-carousel-slide-${index}`" :img-src="image.url" :name="index">
@@ -52,6 +46,13 @@ const screen = useScreen()
52
46
 
53
47
  const carouselImageHeight = 'calc((500/976) * 100vh)'
54
48
 
49
+ const dialogProps = {
50
+ cancel: false,
51
+ ok: false,
52
+ size: 'xl',
53
+ title: 'Galeria de imagens'
54
+ }
55
+
55
56
  const model = computed({
56
57
  get () {
57
58
  return props.modelValue
@@ -15,7 +15,7 @@
15
15
  </slot>
16
16
 
17
17
  <div v-if="hasBadges" class="col-auto items-center q-col-gutter-sm row">
18
- <div v-for="(badge, badgeIndex) in props.badges" :key="badgeIndex">
18
+ <div v-for="(badge, badgeIndex) in normalizedBadges" :key="badgeIndex">
19
19
  <qas-skeleton v-if="props.skeleton" type="QasBadge" />
20
20
 
21
21
  <qas-badge v-else v-bind="badge" />
@@ -73,7 +73,7 @@ const props = defineProps({
73
73
  },
74
74
 
75
75
  badges: {
76
- type: Array,
76
+ type: [Array, Object],
77
77
  default: () => []
78
78
  },
79
79
 
@@ -203,13 +203,15 @@ const actionsComponent = computed(() => {
203
203
  return component.true
204
204
  })
205
205
 
206
+ const normalizedBadges = computed(() => Array.isArray(props.badges) ? props.badges : [props.badges])
207
+
206
208
  const hasActionsComponent = computed(() => {
207
209
  return hasDefaultButton.value || hasDefaultActionsMenu.value || hasDefaultFilters.value
208
210
  })
209
211
 
210
212
  const hasActionsSection = computed(() => !!slots.actions || hasActionsComponent.value)
211
213
 
212
- const hasBadges = computed(() => !!props.badges.length)
214
+ const hasBadges = computed(() => !!normalizedBadges.value.length)
213
215
  const hasLabel = computed(() => !!Object.keys(props.labelProps).length)
214
216
  const hasDefaultButton = computed(() => !!Object.keys(props.buttonProps).length)
215
217
  const hasDefaultFilters = computed(() => !!Object.keys(props.filtersProps).length)
@@ -12,7 +12,7 @@ props:
12
12
  badges:
13
13
  desc: Adiciona badges ao lado do titulo (QasLabel), caso não tenha titulo fica somente as bages acima da descrição.
14
14
  default: []
15
- type: Array
15
+ type: [Array, Object]
16
16
 
17
17
  button-props:
18
18
  desc: Propriedades do QasBtn.
@@ -91,7 +91,8 @@ const model = computed({
91
91
  const drawerProps = computed(() => {
92
92
  return {
93
93
  loading: isMarkingNotificationsAsRead.value,
94
- title: 'Notificações'
94
+ title: 'Notificações',
95
+ size: 'md'
95
96
  }
96
97
  })
97
98
 
@@ -62,7 +62,6 @@ const isForwardButtonDisabled = computed(() => !hasNextRoute.value || isDisabled
62
62
  const drawerProps = computed(() => {
63
63
  return {
64
64
  position: 'right',
65
- maxWidth: '90%',
66
65
  dialogProps: {
67
66
  class: 'pv-layout-overlay-drawer',
68
67
  onHide: closeOverlay,
@@ -54,7 +54,7 @@
54
54
 
55
55
  <template #description>
56
56
  <slot name="dialog-description">
57
- <div v-if="dialogDescription" class="q-mb-xl text-center">
57
+ <div v-if="dialogDescription" class="q-mb-md">
58
58
  {{ dialogDescription }}
59
59
  </div>
60
60
 
@@ -342,8 +342,8 @@ function useSelectDialog () {
342
342
 
343
343
  const defaultDialogProps = computed(() => {
344
344
  return {
345
- useFullMaxWidth: true,
346
-
345
+ size: 'md',
346
+ title: 'Adicionar itens',
347
347
  ...props.dialogProps,
348
348
 
349
349
  onBeforeShow: event => {
@@ -3,12 +3,8 @@
3
3
  <qas-uploader ref="uploader" v-model="model" :add-button-fn="openDialog" :use-resize="false" v-bind="defaultUploaderProps" />
4
4
 
5
5
  <qas-dialog v-model="isOpenedDialog" v-bind="defaultDialogProps">
6
- <template #header>
7
- <div class="text-bold text-center">Insira sua assinatura digital no campo abaixo</div>
8
- </template>
9
-
10
6
  <template #description>
11
- <div :style="signaturePadWidth">
7
+ <div>
12
8
  <qas-signature-pad ref="signaturePadModal" v-model:empty="isEmpty" :height="signaturePadHeight" />
13
9
  </div>
14
10
  </template>
@@ -82,10 +78,11 @@ export default {
82
78
 
83
79
  defaultDialogProps () {
84
80
  return {
85
- maxWidth: '620px',
81
+ title: 'Assinatura digital',
82
+ size: 'md',
86
83
  ...this.dialogProps,
87
84
  ok: {
88
- label: 'Salvar',
85
+ label: 'Assinar',
89
86
  onClick: () => this.getSignatureData()
90
87
  }
91
88
  }
@@ -111,16 +108,6 @@ export default {
111
108
  return sizes.true
112
109
  },
113
110
 
114
- signaturePadWidth () {
115
- const sizes = {
116
- [this.$qas.screen.isSmall]: { width: '100%' },
117
- [this.$qas.screen.isMedium]: { width: '570px' },
118
- [this.$qas.screen.isLarge]: { width: '350px' }
119
- }
120
-
121
- return sizes.true
122
- },
123
-
124
111
  uploaderScope () {
125
112
  return this.$refs?.uploader?.uploader
126
113
  }
@@ -19,7 +19,7 @@
19
19
  <qas-btn v-if="hasButton" class="q-ml-xs" :label="buttonLabel" @click.stop.prevent="toggle" />
20
20
  </div>
21
21
 
22
- <qas-dialog v-model="show" v-bind="defaultProps" aria-label="Diálogo de texto completo" role="dialog">
22
+ <qas-dialog v-model="show" v-bind="defaultProps" aria-label="Diálogo de texto completo" role="dialog" size="md">
23
23
  <template v-if="showDescriptionSlot" #description>
24
24
  <component :is="dialogComponent.is" v-bind="dialogComponent.props" v-model:results="searchModel">
25
25
  <q-list separator>
@@ -225,10 +225,8 @@ function useDialog ({ props, textContent }) {
225
225
 
226
226
  ...props.dialogProps,
227
227
 
228
- card: {
229
- title: props.dialogTitle,
230
- description: description.value
231
- }
228
+ title: props.dialogTitle,
229
+ description: description.value
232
230
  }
233
231
  })
234
232
 
@@ -186,9 +186,9 @@ export default {
186
186
 
187
187
  destroyDialogConfig () {
188
188
  return {
189
- card: {
190
- description: 'Todas as informações serão perdidas. Deseja realmente continuar?'
191
- },
189
+ title: 'Excluir',
190
+ description: 'Todas as informações serão perdidas. Deseja realmente continuar?',
191
+ size: 'md',
192
192
  ok: {
193
193
  label: 'Excluir',
194
194
  onClick: this.destroy
@@ -201,9 +201,7 @@ export default {
201
201
 
202
202
  formDialogConfig () {
203
203
  return {
204
- card: {
205
- title: this.isAdd ? 'Adicionar ramo' : 'Editar ramo'
206
- },
204
+ title: this.isAdd ? 'Adicionar ramo' : 'Editar ramo',
207
205
  ok: {
208
206
  label: 'Salvar',
209
207
  loading: this.isSubmitting
@@ -317,7 +317,17 @@ export default {
317
317
  } = this.$props
318
318
 
319
319
  return {
320
- dialogProps,
320
+ dialogProps: {
321
+ title: 'Editar arquivo',
322
+
323
+ ok: {
324
+ label: 'Salvar',
325
+ ...dialogProps.ok
326
+ },
327
+
328
+ ...dialogProps
329
+ },
330
+
321
331
  fields,
322
332
  formGeneratorProps,
323
333
  galleryCardProps,
@@ -21,16 +21,12 @@ export default function (config = {}) {
21
21
  const { entity, id, url } = deleteActionParams
22
22
 
23
23
  const defaultDialogProps = {
24
- useForm: true,
24
+ useAutoCloseOnOk: false,
25
25
 
26
- ...dialogProps,
27
-
28
- card: {
29
- title: 'Excluir',
30
- description: 'Tem certeza que deseja excluir este item?',
26
+ title: 'Excluir',
27
+ description: 'Tem certeza que deseja excluir este item?',
31
28
 
32
- ...dialogProps.card
33
- },
29
+ ...dialogProps,
34
30
 
35
31
  ok: {
36
32
  label: 'Excluir',