@tagplus/components 0.2.95 → 0.2.99

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
@@ -7,7 +7,7 @@
7
7
  "email": "bruno@tagplus.com.br"
8
8
  }
9
9
  ],
10
- "version": "0.2.95",
10
+ "version": "0.2.99",
11
11
  "main": "./dist/tp.common.js",
12
12
  "directories": {
13
13
  "lib": "src/lib"
@@ -2,6 +2,7 @@
2
2
  <div
3
3
  v-clickoutside="handleClose"
4
4
  class="el-select tp-autosuggest"
5
+ data-is-search="true"
5
6
  :class="[selectSize ? 'el-select--' + selectSize : '']"
6
7
  @click.stop="toggleMenu"
7
8
  >
@@ -127,7 +128,7 @@
127
128
  :class="{'created': item.created}"
128
129
  >
129
130
  <template v-if="item.created">
130
- <a :id="`${_id}-btn-create`"><i class="far fa-plus" /> Cadastrar {{ query ? `"${query}"` : "novo item" }}</a>
131
+ <a :id="`${_id}-btn-create`"><i class="far fa-plus" /> {{ newItem }}</a>
131
132
  </template>
132
133
  <slot
133
134
  v-else
@@ -263,6 +264,15 @@ export default {
263
264
  remoteMethod: {
264
265
  type: Function,
265
266
  required: true
267
+ },
268
+
269
+ /**
270
+ * Informa a mensagem de cadastrar novo item
271
+ */
272
+ msgNewItem: {
273
+ type: String,
274
+ required: false,
275
+ default: 'Cadastrar novo item'
266
276
  }
267
277
  },
268
278
 
@@ -276,6 +286,9 @@ export default {
276
286
  _id() {
277
287
  return this.id || this.$options.name
278
288
  },
289
+ newItem(){
290
+ return this.query ? this.$tpI18n.t('autosuggests.cadastrar', {nameItem: this.query}) : this.$tpI18n.t(`autosuggests.newItem.${this.$parent.$options.name}`)
291
+ },
279
292
 
280
293
  /**
281
294
  * Trata o caso que o autosuggest recebe um objeto com o valueKey e labelKey
@@ -371,9 +384,18 @@ export default {
371
384
  this.createHandler(componenteInputInterno, 'mouseleave', () => {
372
385
  this.tooltipVisible = false
373
386
  })
387
+ document.addEventListener('focusin', this.fixElSelect)
388
+ document.addEventListener('click', this.fixElSelect)
389
+ document.addEventListener('touchstart', this.fixElSelect)
374
390
  })
375
391
  },
376
392
 
393
+ beforeDestroy() {
394
+ document.removeEventListener('focusin', this.fixElSelect, false)
395
+ document.removeEventListener('click', this.fixElSelect, false)
396
+ document.removeEventListener('touchstart', this.fixElSelect, false)
397
+ },
398
+
377
399
  methods: {
378
400
  /**
379
401
  * Adiciona um evento no elemento dinamicamente
@@ -587,8 +609,22 @@ export default {
587
609
  ) {
588
610
  this.checkDefaultFirstOption()
589
611
  }
612
+ },
613
+
614
+ // Correção para habilitar teclado mobile em iOS
615
+ fixElSelect() {
616
+ document.querySelectorAll('.el-select[data-is-search="true"]:hover').forEach(() => {
617
+ let elInput = document.querySelector('.el-select[data-is-search="true"]:hover input[readonly]')
618
+ if (elInput) {
619
+ elInput.readOnly = false
620
+ elInput.blur()
621
+ elInput.focus()
622
+ }
623
+ })
590
624
  }
591
625
  }
626
+
627
+
592
628
  }
593
629
  </script>
594
630
 
@@ -0,0 +1,45 @@
1
+ <template>
2
+ <div>
3
+ <div>Autosuggest 1</div>
4
+ <tp-autosuggest
5
+ :id="_id"
6
+ :value="value"
7
+ :placeholder="$t($options.placeholder)"
8
+ :label-key="$options.labelKey"
9
+ :remote-method="autosuggestMixin_getList"
10
+ :suggestions="list"
11
+ :loading="loading"
12
+ :load-on-create="isLoadOnCreate"
13
+ :disabled="_disabled"
14
+ :allow-create="false"
15
+ @input="autosuggestMixin_input"
16
+ @handleOptionClick="autosuggestMixin_handleCreate"
17
+ @change="autosuggestMixin_handleChange"
18
+ />
19
+ </div>
20
+ </template>
21
+
22
+ <script>
23
+ import AutosuggestsMixin from './autosuggest'
24
+
25
+ export default {
26
+ valueKey: 'id',
27
+ labelKey: 'name',
28
+ placeholder: 'common.placeholder',
29
+
30
+ mixins: [AutosuggestsMixin],
31
+
32
+ data() {
33
+ return {
34
+ params: {
35
+ uri: 'https://jsonplaceholder.typicode.com/users',
36
+ suggestions: [],
37
+ loading: true
38
+ }
39
+ }
40
+ }
41
+ }
42
+ </script>
43
+
44
+ <style lang="scss" scoped>
45
+ </style>
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Mixin contendo metodos para autosuggests
3
+ */
4
+ export default {
5
+ props: {
6
+ /**
7
+ * @vuese
8
+ * id do css para teste unitário
9
+ */
10
+ id: {
11
+ type: String
12
+ },
13
+
14
+ value: {
15
+ type: [Number, String, Object, Boolean, Array],
16
+ default: ''
17
+ },
18
+
19
+ // Desativa o loader global ao carregar lista
20
+ disableLoader: {
21
+ type: Boolean,
22
+ default: false
23
+ },
24
+
25
+ // Indica se primeiro item da lista deve ser retornado por padrão
26
+ noDefault: {
27
+ type: Boolean,
28
+ default: false
29
+ },
30
+
31
+ /**
32
+ * Se a lista vai ser carregada no create ou o labelKey não venha por padrão na requisição loadOnCreate = true,
33
+ * caso deva ser carregada quando o usuário clica deixar vazio ou false.
34
+ */
35
+ loadOnCreate: {
36
+ type: Boolean,
37
+ default: null
38
+ },
39
+
40
+ // Indicia se o campo deve ser desabilitado
41
+ disabled: {
42
+ type: Boolean,
43
+ default: false
44
+ },
45
+
46
+ // Se o valor desse autosuggest for obrigatório se o value for vazio o after get vai selecionar a primeira opção
47
+ isRequired: {
48
+ type: Boolean,
49
+ default: false
50
+ },
51
+
52
+ allowCreate: {
53
+ type: Boolean,
54
+ default: true
55
+ },
56
+ valueKey: {
57
+ type: String,
58
+ default: 'id'
59
+ }
60
+ },
61
+
62
+ data () {
63
+ return {
64
+ header: {
65
+ 'X-Skip-Total-Pages': true
66
+ },
67
+ params: {}
68
+ },
69
+ doEmptyRequest: false,
70
+ list: [],
71
+ loading: true,
72
+ isLoadOnCreate: true,
73
+ modal: false,
74
+ autosuggest: false, // endpoint /autosuggest/*
75
+ customEndpoint: false // buscar em qualquer endpoint
76
+ }
77
+ },
78
+
79
+ computed: {
80
+ _id() {
81
+ return this.id || this.$options.name
82
+ },
83
+
84
+ /** desabilita o componente quando recebe disable true ou não tem permissão no form de incluir ou alterar */
85
+ _disabled() {
86
+ if (this.disabled) {
87
+ return true
88
+ } else if (this.disableForm) {
89
+ return true
90
+ }
91
+ return false
92
+ },
93
+
94
+ /** @vuese
95
+ * Action para buscar em stores customizadas/dinâmicas, por padrão pega do $options.listAction
96
+ */
97
+ listAction() {
98
+ return this.customAction ? this.customAction : this.$options.listAction
99
+ },
100
+
101
+ /** @vuese
102
+ * Retorna a URL a ser usada para buscar a lista caso não esteja definida no $options
103
+ * Passam aqui as urls customizadas, do /autosuggests, /entidade/categorias etc
104
+ */
105
+ customUrl() {
106
+ if (this.customEndpoint) {
107
+ return this.customEndpoint
108
+ } else if (this.autosuggest) {
109
+ return `/autosuggests/${this.$options.autosuggest}`
110
+ } else {
111
+ return false
112
+ }
113
+ },
114
+
115
+ _valueKey() {
116
+ return this.$options.valueKey || 'id'
117
+ },
118
+
119
+ _labelKey() {
120
+ return this.$options.labelKey || 'descricao'
121
+ }
122
+ },
123
+
124
+ /**
125
+ * Ao montar componente, inicia o loading
126
+ */
127
+ mounted() {
128
+ if (!this.disableLoader && this.isLoadOnCreate) {
129
+ this.startLoading(this.$options.name)
130
+ }
131
+ },
132
+
133
+ created() {
134
+ if (this.$parent.$options.name.substring(0, 6) === 'Filtro') {
135
+ this.isLoadOnCreate = false
136
+ } else if (this.loadOnCreate !== null) { // Se recebeu loadOnCreate via prop leva precedência
137
+ // Se for campo obrigatório e não vier o value inicial força a busca na api
138
+ if (this.isRequired && typeof this.value === 'undefined') {
139
+ this.isLoadOnCreate = true
140
+ } else {
141
+ this.isLoadOnCreate = this.loadOnCreate
142
+ }
143
+ } else if (this.$route && !isNaN(this.$route.params.id)) {
144
+ // Se está num form com ID só carrega lista quando clica
145
+ this.isLoadOnCreate = false
146
+ }
147
+
148
+ // Caso tenha value no created a primeira busca quando clica é vazia
149
+ this.doEmptyRequest = (typeof this.value !== 'undefined')
150
+ },
151
+
152
+ methods: {
153
+ /**
154
+ * HOOK executado quando getList é chamado
155
+ */
156
+ autosuggestMixin_beforeGet() { },
157
+
158
+ /**
159
+ * HOOK executado quando getList é chamado
160
+ */
161
+ autosuggestMixin_afterGet() { },
162
+
163
+ /**
164
+ * HOOK executado quando handleClose é chamado
165
+ */
166
+ autosuggestMixin_beforeHandleClose() { },
167
+
168
+ /**
169
+ * Realiza chamada da action no endpoint
170
+ * @param {*} busca valor recebido no autosuggest
171
+ */
172
+ async autosuggestMixin_getList(busca) {
173
+ busca ||= ''
174
+
175
+ if (!Array.isArray(busca) && (busca !== '' || this.config.params.q !== busca)) {
176
+ this.config.params.q = busca
177
+ }
178
+
179
+ // Tratamento caso clique no primeiro autosuggest daquele tipo com loadOnCreate false e tenha um valor padrão
180
+ if (this.doEmptyRequest) {
181
+ this.config.params.q = ''
182
+ this.doEmptyRequest = false
183
+ }
184
+
185
+ // Hook executado antes de recuperar a lista
186
+ this.autosuggestMixin_beforeGet()
187
+
188
+ this.loading = true
189
+
190
+ // Se buscar em /autosuggest ou url dinâmica/customizada pega a url aqui
191
+ this.config.customUrl = this.customUrl
192
+
193
+ try {
194
+ await this.axios
195
+ .get(`${this.params.uri}?q=${busca}`)
196
+ .then(res => {
197
+ this.list = res.data
198
+ })
199
+ // eslint-disable-next-line no-unused-vars
200
+ .catch(err => {
201
+ this.list = []
202
+ }).finally(() => {
203
+ this.loading = false
204
+ })
205
+ } catch (err) {
206
+ // this.$TpError(err)
207
+ } finally {
208
+ // this.finishLoading(this.$options.name)
209
+ }
210
+ },
211
+
212
+ /**
213
+ * Metodo para lidar com a criação de um novo registro
214
+ * Exibe o modal por padrão
215
+ * @param {*} evt Evento de criação
216
+ */
217
+ autosuggestMixin_handleCreate(evt) {
218
+ if (evt.created) {
219
+ this.modal = true
220
+ }
221
+ },
222
+
223
+ /**
224
+ * Método para lidar com o fechamento do modal
225
+ * Por padrão recarrega a lista e recupera o valor criado no modal, caso haja
226
+ * @param {*} val Valor recebido do modal
227
+ */
228
+ async autosuggestMixin_handleClose(val) {
229
+ // Fecha o modal primeiro para diminuir tempo de transição
230
+ this.modal = false
231
+
232
+ if (val.id !== 0) {
233
+ const id = val.id?.id !== undefined ? val.id.id : val.id
234
+
235
+ await this.autosuggestMixin_getList('')
236
+
237
+ this.$emit('input', val.id)
238
+
239
+ const item = this.list.filter((value) => {
240
+ return id === value.id
241
+ })
242
+
243
+ this.$emit('change', item[0])
244
+ }
245
+ },
246
+
247
+ /**
248
+ * Evento para tratamento do valor
249
+ * Devolve o valor recebido para o v-model no componente pai
250
+ * @param {*} evt
251
+ */
252
+ autosuggestMixin_input(evt) {
253
+ this.$emit('input', evt)
254
+ this.autosuggestMixin_inputFullItem(evt)
255
+ },
256
+
257
+ /**
258
+ * Evento Input com os dados completos do item selecionado
259
+ * @param {*} evt
260
+ */
261
+ autosuggestMixin_inputFullItem(evt) {
262
+ let res = []
263
+
264
+ if (Array.isArray(evt)) {
265
+ evt.map((idValue) => {
266
+ this.list.map((item2) => {
267
+ if (idValue === item2[this._valueKey]) {
268
+ res.push(item2)
269
+ }
270
+ return item2
271
+ })
272
+ })
273
+ } else {
274
+ res = evt
275
+ }
276
+ this.$emit('inputFullItem', res)
277
+ },
278
+
279
+ autosuggestMixin_handleChange(val) {
280
+ if (val !== null && val !== undefined) {
281
+ const item = this.list.filter((value) => {
282
+ return val === value.id
283
+ })
284
+
285
+ this.$emit('change', item[0])
286
+ } else {
287
+ this.$emit('change', val)
288
+ }
289
+ }
290
+ }
291
+ }
@@ -459,8 +459,10 @@ export default {
459
459
 
460
460
  this.value.forEach((item,key) => {
461
461
  if (item[this.valueKey]) {
462
- this.selectedLabelsArray.push(item)
463
462
  this.value[key] = item[this.valueKey]
463
+ if(item[this.labelKey]) {
464
+ this.selectedLabelsArray.push(item)
465
+ }
464
466
  }
465
467
  })
466
468
 
@@ -523,7 +525,7 @@ export default {
523
525
  else if (val && typeof val === 'object') {
524
526
  // Se val for Object converte para outro tipo
525
527
  if (!val[this.valueKey]) {
526
- throw new TypeError('Autosuggest this.value doesn\'t have a valueKey \'' + this.valueKey + '\' key')
528
+ throw new TypeError('Multisuggest option doesn\'t have a valueKey \'' + this.valueKey + '\' key')
527
529
  } else {
528
530
  // Se mandou a label no objeto value
529
531
  if (val[this.labelKey]) {