@tagplus/components 4.7.12 → 5.1.0
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/dist/tp.common.js +2 -1
- package/dist/tp.common.js.LICENSE.txt +9 -0
- package/dist/tp.common.js.map +1 -1
- package/dist/tp.common.lang-tp-en-js.js +2 -0
- package/dist/tp.common.lang-tp-en-js.js.map +1 -0
- package/dist/tp.css +11 -167
- package/dist/tp.umd.js +2 -1
- package/dist/tp.umd.js.LICENSE.txt +9 -0
- package/dist/tp.umd.js.map +1 -1
- package/dist/tp.umd.lang-tp-en-js.js +2 -0
- package/dist/tp.umd.lang-tp-en-js.js.map +1 -0
- package/dist/tp.umd.min.js +2 -1
- package/dist/tp.umd.min.js.LICENSE.txt +9 -0
- package/dist/tp.umd.min.js.map +1 -1
- package/dist/tp.umd.min.lang-tp-en-js.js +2 -0
- package/dist/tp.umd.min.lang-tp-en-js.js.map +1 -0
- package/package.json +51 -50
- package/src/assets/scss/_fonts.scss +24 -23
- package/src/assets/scss/_helpers.scss +4 -0
- package/src/assets/scss/_mixins.scss +2 -2
- package/src/assets/scss/_overrides.scss +5 -52
- package/src/assets/scss/_resass.scss +21 -12
- package/src/assets/scss/_variables.scss +0 -1
- package/src/assets/scss/index.scss +1 -4
- package/src/components/Autosuggest/Autosuggest.vue +340 -767
- package/src/components/Autosuggest/Multisuggest.vue +22 -0
- package/src/components/Autosuggest/autosuggest-props.js +210 -0
- package/src/components/Autosuggest/autosuggest-style.scss +127 -0
- package/src/components/Autosuggest/core.js +63 -0
- package/src/components/Autosuggest/index.js +2 -0
- package/src/components/Autosuggest/multisuggest-props.js +9 -0
- package/src/components/Autosuggest/option.vue +136 -0
- package/src/components/Autosuggest/select-dropdown.vue +64 -0
- package/src/components/Autosuggest/useOption.js +120 -0
- package/src/components/Autosuggest/useSelect.js +1133 -0
- package/src/components/AutosuggestTest.vue +56 -0
- package/src/components/CardExemplo.vue +49 -0
- package/src/components/CodeSample.vue +78 -0
- package/src/components/Inline/Inline.vue +24 -32
- package/src/components/InputNumber/InputNumber.vue +329 -378
- package/src/components/InputNumber/input-number.js +135 -0
- package/src/components/Loader/Loader.vue +42 -53
- package/src/components/Loader/animations.scss +13 -0
- package/src/components/Money/Money.vue +11 -20
- package/src/components/Multisuggest/index.js +2 -3
- package/src/components/MultisuggestTest.vue +56 -0
- package/src/components/OptionsList/OptionsList.vue +7 -6
- package/src/components/OptionsListItem/OptionsListItem.vue +46 -42
- package/src/components/Percent/Percent.vue +8 -14
- package/src/components/Skeleton/Skeleton.vue +16 -11
- package/src/components/Step/Step.vue +42 -35
- package/src/components/Steps/Steps.vue +4 -7
- package/src/components/TesteToCurrency.vue +171 -0
- package/src/components/Tip/Tip.vue +45 -30
- package/src/components/ValueSelector.vue +60 -0
- package/src/components/autosuggestMixin.js +301 -0
- package/src/components/index.js +4 -1
- package/src/locale/i18n.js +162 -0
- package/src/locale/lang/en.js +3 -2
- package/src/locale/lang/pt-br.js +3 -2
- package/src/main.js +8 -14
- package/src/mixins/floatFormatter.js +12 -16
- package/src/plugins/currency.js +100 -0
- package/src/utils/browser.js +6 -0
- package/src/utils/constants.js +3 -0
- package/src/utils/error.js +22 -0
- package/src/utils/filters.js +1 -14
- package/src/utils/helpers.js +41 -0
- package/src/utils/i18n.js +2 -0
- package/src/utils/icon.js +35 -0
- package/src/utils/index.js +20 -0
- package/src/utils/objects.js +17 -0
- package/src/utils/runtime.js +86 -0
- package/src/utils/scroll.js +100 -0
- package/src/utils/strings.js +17 -0
- package/src/utils/style.js +80 -0
- package/src/utils/types.js +39 -0
- package/src/utils/use-derived-namespace.js +112 -0
- package/src/utils/use-form-common-props.js +41 -0
- package/src/utils/use-form-item.js +80 -0
- package/src/utils/use-id.js +40 -0
- package/src/utils/use-input.js +33 -0
- package/src/components/Dialog/Dialog.vue +0 -253
- package/src/components/Dialog/index.js +0 -3
- package/src/components/Multisuggest/Multisuggest.vue +0 -858
- package/src/locale/index.js +0 -78
- package/src/mixins/locale.js +0 -9
- package/src/utils/currency.js +0 -180
|
@@ -0,0 +1,1133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computed,
|
|
3
|
+
nextTick,
|
|
4
|
+
onMounted,
|
|
5
|
+
reactive,
|
|
6
|
+
ref,
|
|
7
|
+
watch,
|
|
8
|
+
watchEffect,
|
|
9
|
+
getCurrentInstance,
|
|
10
|
+
} from 'vue'
|
|
11
|
+
// eslint-disable-next-line vue/prefer-import-from-vue
|
|
12
|
+
import { isArray, isObject, toRawType } from '@vue/shared'
|
|
13
|
+
import {
|
|
14
|
+
findLastIndex,
|
|
15
|
+
get,
|
|
16
|
+
isEqual,
|
|
17
|
+
debounce as lodashDebounce,
|
|
18
|
+
} from 'lodash-unified'
|
|
19
|
+
import { useResizeObserver } from '@vueuse/core'
|
|
20
|
+
const UPDATE_MODEL_EVENT = 'update:modelValue'
|
|
21
|
+
const CHANGE_EVENT = 'change'
|
|
22
|
+
const EVENT_CODE = {
|
|
23
|
+
tab: 'Tab',
|
|
24
|
+
enter: 'Enter',
|
|
25
|
+
space: 'Space',
|
|
26
|
+
left: 'ArrowLeft', // 37
|
|
27
|
+
up: 'ArrowUp', // 38
|
|
28
|
+
right: 'ArrowRight', // 39
|
|
29
|
+
down: 'ArrowDown', // 40
|
|
30
|
+
esc: 'Escape',
|
|
31
|
+
delete: 'Delete',
|
|
32
|
+
backspace: 'Backspace',
|
|
33
|
+
numpadEnter: 'NumpadEnter',
|
|
34
|
+
pageUp: 'PageUp',
|
|
35
|
+
pageDown: 'PageDown',
|
|
36
|
+
home: 'Home',
|
|
37
|
+
end: 'End',
|
|
38
|
+
}
|
|
39
|
+
import {
|
|
40
|
+
ValidateComponentsMap,
|
|
41
|
+
debugWarn,
|
|
42
|
+
isClient,
|
|
43
|
+
isFunction,
|
|
44
|
+
isNumber,
|
|
45
|
+
isString,
|
|
46
|
+
scrollIntoView,
|
|
47
|
+
useInput,
|
|
48
|
+
useFormItem,
|
|
49
|
+
useFormItemInputId,
|
|
50
|
+
useFormSize,
|
|
51
|
+
} from '@/utils'
|
|
52
|
+
import {
|
|
53
|
+
useFocusController,
|
|
54
|
+
useId,
|
|
55
|
+
useLocale,
|
|
56
|
+
useNamespace,
|
|
57
|
+
} from 'element-plus/es/hooks/index'
|
|
58
|
+
|
|
59
|
+
const MINIMUM_INPUT_WIDTH = 11
|
|
60
|
+
|
|
61
|
+
export const useSelect = (props, { emit }) => {
|
|
62
|
+
const { t } = useLocale()
|
|
63
|
+
const contentId = useId()
|
|
64
|
+
const nsSelect = useNamespace('select')
|
|
65
|
+
const nsInput = useNamespace('input')
|
|
66
|
+
|
|
67
|
+
const states = reactive({
|
|
68
|
+
inputValue: '',
|
|
69
|
+
options: new Map(),
|
|
70
|
+
cachedOptions: new Map(),
|
|
71
|
+
disabledOptions: new Map(),
|
|
72
|
+
optionValues: [], // sorted value of options
|
|
73
|
+
selected: props.multiple ? [] : ({}),
|
|
74
|
+
selectionWidth: 0,
|
|
75
|
+
calculatorWidth: 0,
|
|
76
|
+
collapseItemWidth: 0,
|
|
77
|
+
selectedLabel: '',
|
|
78
|
+
hoveringIndex: -1,
|
|
79
|
+
previousQuery: null,
|
|
80
|
+
inputHovering: false,
|
|
81
|
+
menuVisibleOnFocus: false,
|
|
82
|
+
isBeforeHide: false,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
// template refs
|
|
86
|
+
const selectRef = ref(null)
|
|
87
|
+
const selectionRef = ref(null)
|
|
88
|
+
const tooltipRef = ref(null)
|
|
89
|
+
const inputRef = ref(null)
|
|
90
|
+
const calculatorRef = ref(null)
|
|
91
|
+
const suffixRef = ref(null)
|
|
92
|
+
const menuRef = ref(null)
|
|
93
|
+
const collapseItemRef = ref(null)
|
|
94
|
+
const scrollbarRef = ref(null)
|
|
95
|
+
|
|
96
|
+
const { wrapperRef, isFocused, handleFocus, handleBlur } = useFocusController(
|
|
97
|
+
inputRef,
|
|
98
|
+
{
|
|
99
|
+
afterFocus () {
|
|
100
|
+
if (props.automaticDropdown && !expanded.value) {
|
|
101
|
+
expanded.value = true
|
|
102
|
+
states.menuVisibleOnFocus = true
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
beforeBlur (event) {
|
|
106
|
+
return (
|
|
107
|
+
tooltipRef.value?.isFocusInsideContent(event)
|
|
108
|
+
)
|
|
109
|
+
},
|
|
110
|
+
afterBlur () {
|
|
111
|
+
expanded.value = false
|
|
112
|
+
states.menuVisibleOnFocus = false
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
// the controller of the expanded popup
|
|
118
|
+
const expanded = ref(false)
|
|
119
|
+
const hoverOption = ref()
|
|
120
|
+
|
|
121
|
+
const { form, formItem } = useFormItem()
|
|
122
|
+
const { inputId } = useFormItemInputId(props, {
|
|
123
|
+
formItemContext: formItem,
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
const selectDisabled = computed(() => props.disabled || form?.disabled)
|
|
127
|
+
|
|
128
|
+
const hasModelValue = computed(() => {
|
|
129
|
+
return props.multiple
|
|
130
|
+
? isArray(props.modelValue) && props.modelValue.length > 0
|
|
131
|
+
: props.modelValue !== undefined &&
|
|
132
|
+
props.modelValue !== null &&
|
|
133
|
+
props.modelValue !== ''
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @tagplus
|
|
138
|
+
* Sobrescrito para mostrar o botão sem hover, para funcionar no mobile
|
|
139
|
+
*/
|
|
140
|
+
const showClose = computed(() => {
|
|
141
|
+
const criteria =
|
|
142
|
+
props.clearable &&
|
|
143
|
+
!selectDisabled.value &&
|
|
144
|
+
hasModelValue.value
|
|
145
|
+
return criteria
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @tagplus
|
|
150
|
+
* Sobrescrito para remover parâmetro suffixTransition
|
|
151
|
+
*/
|
|
152
|
+
const iconReverse = computed(() => {
|
|
153
|
+
return expanded.value ? 'is-reverse' : ''
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @tagplus
|
|
158
|
+
* Trata o caso que o autosuggest recebe um objeto com o valueKey e labelKey
|
|
159
|
+
*/
|
|
160
|
+
const formattedValue = computed(() => {
|
|
161
|
+
let newVal = ''
|
|
162
|
+
if (typeof props.modelValue === 'boolean') {
|
|
163
|
+
newVal = ''
|
|
164
|
+
} else if (Array.isArray(props.modelValue)) {
|
|
165
|
+
newVal = props.modelValue
|
|
166
|
+
} else if (props.modelValue && typeof props.modelValue === 'object') {
|
|
167
|
+
if (!Object.keys(newVal).length) { return {} }
|
|
168
|
+
if (!props.modelValue[props.valueKey] && props.modelValue[props.valueKey] !== '') {
|
|
169
|
+
if (process.env.DEBUG === 'true') {
|
|
170
|
+
const instance = getCurrentInstance()
|
|
171
|
+
console.error(`Componente '${instance.type.componentName}' option doesn't have a valueKey '${props.valueKey}' key` )
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
newVal = props.modelValue[props.valueKey]
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return newVal
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const validateState = computed(() => formItem?.validateState || '')
|
|
182
|
+
const validateIcon = computed(
|
|
183
|
+
() => ValidateComponentsMap[validateState.value]
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @tagplus
|
|
188
|
+
* Sobrescrito porque é sempre remote
|
|
189
|
+
*/
|
|
190
|
+
const debounce = 300
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @tagplus
|
|
194
|
+
* Sobrescrito para remover parâmetro filterable e remote
|
|
195
|
+
* Altera a lang padrão de "Sem dados" para não ter que preencher a prop em todas instâncias do projeto 2.0
|
|
196
|
+
*/
|
|
197
|
+
const emptyText = computed(() => {
|
|
198
|
+
if (props.loading) {
|
|
199
|
+
return props.loadingText || t('el.select.loading')
|
|
200
|
+
} else {
|
|
201
|
+
if (!states.inputValue && states.options.size === 0)
|
|
202
|
+
return false
|
|
203
|
+
if (
|
|
204
|
+
states.inputValue &&
|
|
205
|
+
states.options.size > 0 &&
|
|
206
|
+
filteredOptionsCount.value === 0
|
|
207
|
+
) {
|
|
208
|
+
return props.noMatchText || t('el.select.noMatch')
|
|
209
|
+
}
|
|
210
|
+
if (states.options.size === 0) {
|
|
211
|
+
// @TODO Verificar se mostrará no projeto nuxt
|
|
212
|
+
return props.noDataText || t('autosuggests.sem_dados')
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return null
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
const filteredOptionsCount = computed(
|
|
219
|
+
() => optionsArray.value.filter((option) => option.visible).length
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
const optionsArray = computed(() => {
|
|
223
|
+
const list = Array.from(states.options.values())
|
|
224
|
+
const newList = []
|
|
225
|
+
states.optionValues.forEach((item) => {
|
|
226
|
+
const index = list.findIndex((i) => i.value === item)
|
|
227
|
+
if (index > -1) {
|
|
228
|
+
newList.push(list[index])
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
return newList.length >= list.length ? newList : list
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
const cachedOptionsArray = computed(() =>
|
|
235
|
+
Array.from(states.cachedOptions.values())
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* @tagplus
|
|
240
|
+
* Sobrescrito para remover parâmetro filterable
|
|
241
|
+
*/
|
|
242
|
+
const showNewOption = computed(() => {
|
|
243
|
+
const hasExistingOption = optionsArray.value
|
|
244
|
+
.filter((option) => {
|
|
245
|
+
return !option.created
|
|
246
|
+
})
|
|
247
|
+
.some((option) => {
|
|
248
|
+
return option.currentLabel === states.inputValue
|
|
249
|
+
})
|
|
250
|
+
return (
|
|
251
|
+
props.allowCreate &&
|
|
252
|
+
states.inputValue !== '' &&
|
|
253
|
+
!hasExistingOption
|
|
254
|
+
)
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* @tagplus
|
|
259
|
+
* Sobrescrito porque será sempre filterable com remoteMethod
|
|
260
|
+
*/
|
|
261
|
+
const updateOptions = () => {
|
|
262
|
+
return
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const selectSize = useFormSize()
|
|
266
|
+
|
|
267
|
+
const collapseTagSize = computed(() =>
|
|
268
|
+
['small'].includes(selectSize.value) ? 'small' : 'default'
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @tagplus
|
|
273
|
+
* Sobrescrito para alterar o doRequest
|
|
274
|
+
*/
|
|
275
|
+
const dropdownMenuVisible = computed({
|
|
276
|
+
get () {
|
|
277
|
+
return expanded.value && emptyText.value !== false
|
|
278
|
+
},
|
|
279
|
+
set (val) {
|
|
280
|
+
states.doRequest = true
|
|
281
|
+
expanded.value = val
|
|
282
|
+
},
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @tagplus
|
|
287
|
+
* Sobrescrito porque não tem mais filterable
|
|
288
|
+
*/
|
|
289
|
+
const shouldShowPlaceholder = computed(() => {
|
|
290
|
+
if (isArray(props.modelValue)) {
|
|
291
|
+
return props.modelValue.length === 0 && !states.inputValue
|
|
292
|
+
}
|
|
293
|
+
return !states.inputValue
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
const currentPlaceholder = computed(() => {
|
|
297
|
+
const _placeholder = props.placeholder ?? t('el.select.placeholder')
|
|
298
|
+
return props.multiple || !hasModelValue.value
|
|
299
|
+
? _placeholder
|
|
300
|
+
: states.selectedLabel
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @tagplus
|
|
305
|
+
* Criado para montar a lista com ou sem "Cadastrar Novo Item"
|
|
306
|
+
*/
|
|
307
|
+
const suggestionsList = computed(() => {
|
|
308
|
+
if (props.loading) return []
|
|
309
|
+
|
|
310
|
+
// transformando em Array
|
|
311
|
+
const list =
|
|
312
|
+
typeof props.suggestions === 'object'
|
|
313
|
+
? Object.values(props.suggestions)
|
|
314
|
+
: props.suggestions
|
|
315
|
+
|
|
316
|
+
if (props.allowCreate) {
|
|
317
|
+
const createdSuggestion = { created: true }
|
|
318
|
+
|
|
319
|
+
createdSuggestion[props.labelKey] = states.inputValue !== '' ? states.inputValue : null
|
|
320
|
+
createdSuggestion[props.valueKey] = createdSuggestion[props.labelKey]
|
|
321
|
+
list.push(createdSuggestion)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return list
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* @tagplus
|
|
329
|
+
* Criado para formatar o newItem
|
|
330
|
+
*/
|
|
331
|
+
const newItem = computed(() => {
|
|
332
|
+
const instance = getCurrentInstance()
|
|
333
|
+
// @todo rever se vai funcionar no nuxt
|
|
334
|
+
return states.inputValue
|
|
335
|
+
? t('autosuggests.cadastrar', { nameItem: states.inputValue })
|
|
336
|
+
: t(`autosuggests.newItem.${instance.type.componentName}`)
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* @tagplus
|
|
341
|
+
* Criado para conseguir ter mais de um componente na mesma página com ids diferentes
|
|
342
|
+
*/
|
|
343
|
+
const myId = computed(() => {
|
|
344
|
+
const instance = getCurrentInstance()
|
|
345
|
+
return props.id || instance.type.componentName
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* @tagplus
|
|
350
|
+
* Criado para formatar corretamente a option visível quando recebe number e carregou a lista
|
|
351
|
+
*/
|
|
352
|
+
const firstValueStopHandle = watch(
|
|
353
|
+
() => props.suggestions,
|
|
354
|
+
(val, oldVal) => {
|
|
355
|
+
if (!isNumber(props.modelValue)) {
|
|
356
|
+
firstValueStopHandle()
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (val.length){
|
|
360
|
+
setSelected()
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
firstValueStopHandle()
|
|
364
|
+
}
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* @tagplus
|
|
369
|
+
* Sobrescrito para remover prop filterable
|
|
370
|
+
*/
|
|
371
|
+
watch(
|
|
372
|
+
() => props.modelValue,
|
|
373
|
+
(val, oldVal) => {
|
|
374
|
+
if (props.multiple) {
|
|
375
|
+
if (!props.reserveKeyword) {
|
|
376
|
+
states.inputValue = ''
|
|
377
|
+
handleQueryChange('')
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
setSelected()
|
|
381
|
+
if (!isEqual(val, oldVal) && props.validateEvent) {
|
|
382
|
+
formItem?.validate('change').catch((err) => debugWarn(err))
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
flush: 'post',
|
|
387
|
+
deep: true,
|
|
388
|
+
}
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
watch(
|
|
392
|
+
() => expanded.value,
|
|
393
|
+
(val) => {
|
|
394
|
+
if (val) {
|
|
395
|
+
handleQueryChange(states.inputValue)
|
|
396
|
+
} else {
|
|
397
|
+
states.inputValue = ''
|
|
398
|
+
states.previousQuery = null
|
|
399
|
+
states.isBeforeHide = true
|
|
400
|
+
}
|
|
401
|
+
emit('visible-change', val)
|
|
402
|
+
}
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* @tagplus
|
|
407
|
+
* Sobrescrito para remover prop filterable e remote
|
|
408
|
+
*/
|
|
409
|
+
watch(
|
|
410
|
+
// fix `Array.prototype.push/splice/..` cannot trigger non-deep watcher
|
|
411
|
+
// https://github.com/vuejs/vue-next/issues/2116
|
|
412
|
+
() => states.options.entries(),
|
|
413
|
+
() => {
|
|
414
|
+
if (!isClient) return
|
|
415
|
+
|
|
416
|
+
if (props.defaultFirstOption && filteredOptionsCount.value ) {
|
|
417
|
+
checkDefaultFirstOption()
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
flush: 'post',
|
|
422
|
+
}
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
watch(
|
|
426
|
+
() => states.hoveringIndex,
|
|
427
|
+
(val) => {
|
|
428
|
+
if (isNumber(val) && val > -1) {
|
|
429
|
+
hoverOption.value = optionsArray.value[val] || {}
|
|
430
|
+
} else {
|
|
431
|
+
hoverOption.value = {}
|
|
432
|
+
}
|
|
433
|
+
optionsArray.value.forEach((option) => {
|
|
434
|
+
option.hover = hoverOption.value === option
|
|
435
|
+
})
|
|
436
|
+
}
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
watchEffect(() => {
|
|
440
|
+
// Anything could cause options changed, then update options
|
|
441
|
+
// If you want to control it by condition, write here
|
|
442
|
+
if (states.isBeforeHide) return
|
|
443
|
+
updateOptions()
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* @tagplus
|
|
448
|
+
* Sobrescrito para remover parâmetro filterable e remote
|
|
449
|
+
* @param {String} val
|
|
450
|
+
*/
|
|
451
|
+
const handleQueryChange = (val) => {
|
|
452
|
+
// Correção aqui para forçar primeeira request com createOnLoad = false
|
|
453
|
+
if (!states.doRequest && states.previousQuery === val) { return }
|
|
454
|
+
if (states.previousQuery === val) { return }
|
|
455
|
+
|
|
456
|
+
states.previousQuery = val
|
|
457
|
+
if (isFunction(props.remoteMethod)) {
|
|
458
|
+
props.remoteMethod(val)
|
|
459
|
+
}
|
|
460
|
+
if (props.defaultFirstOption) {
|
|
461
|
+
nextTick(checkDefaultFirstOption)
|
|
462
|
+
} else {
|
|
463
|
+
nextTick(updateHoveringIndex)
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* find and highlight first option as default selected
|
|
469
|
+
* @remark
|
|
470
|
+
* - if the first option in dropdown list is user-created,
|
|
471
|
+
* it would be at the end of the optionsArray
|
|
472
|
+
* so find it and set hover.
|
|
473
|
+
* (NOTE: there must be only one user-created option in dropdown list with query)
|
|
474
|
+
* - if there's no user-created option in list, just find the first one as usual
|
|
475
|
+
* (NOTE: exclude options that are disabled or in disabled-group)
|
|
476
|
+
*/
|
|
477
|
+
const checkDefaultFirstOption = () => {
|
|
478
|
+
const optionsInDropdown = optionsArray.value.filter(
|
|
479
|
+
(n) => n.visible && !n.disabled && !n.states.groupDisabled
|
|
480
|
+
)
|
|
481
|
+
const userCreatedOption = optionsInDropdown.find((n) => n.created)
|
|
482
|
+
const firstOriginOption = optionsInDropdown[0]
|
|
483
|
+
states.hoveringIndex = getValueIndex(
|
|
484
|
+
optionsArray.value,
|
|
485
|
+
userCreatedOption || firstOriginOption
|
|
486
|
+
)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const setSelected = () => {
|
|
490
|
+
if (!props.multiple) {
|
|
491
|
+
const option = getOption(props.modelValue)
|
|
492
|
+
states.selectedLabel = option.currentLabel
|
|
493
|
+
states.selected = option
|
|
494
|
+
return
|
|
495
|
+
} else {
|
|
496
|
+
states.selectedLabel = ''
|
|
497
|
+
}
|
|
498
|
+
const result = []
|
|
499
|
+
if (isArray(props.modelValue)) {
|
|
500
|
+
props.modelValue.forEach((value) => {
|
|
501
|
+
result.push(getOption(value))
|
|
502
|
+
})
|
|
503
|
+
}
|
|
504
|
+
states.selected = result
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* @tagplus
|
|
509
|
+
* Criado para encontrar o objeto referente à value nas suggestions,
|
|
510
|
+
* comparando o value com o valor de item[valueKey]
|
|
511
|
+
* @param {String|Number} value
|
|
512
|
+
* @return {Object}
|
|
513
|
+
*/
|
|
514
|
+
const getObjectFromSuggestions = (value) => {
|
|
515
|
+
// Tenta transformar modelVal Number em Object com valueKey
|
|
516
|
+
for (let i = 0; i < props.suggestions.length; i++) {
|
|
517
|
+
const item = props.suggestions[i]
|
|
518
|
+
if (item[props.valueKey] === value){
|
|
519
|
+
return {
|
|
520
|
+
...item,
|
|
521
|
+
[props.valueKey]: JSON.parse(JSON.stringify(value)),
|
|
522
|
+
[props.labelKey]: item[props.labelKey]
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
[props.valueKey]: value,
|
|
529
|
+
[props.labelKey]: value
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* @tagplus
|
|
535
|
+
* Criado para extrair dados da modelValue dos suggestions para criar a opção clicável e mostrar a label corretamente
|
|
536
|
+
* @param {Boolean, Object, Array, Number} modelValue
|
|
537
|
+
*/
|
|
538
|
+
const getValueLabelForOption = (modelValue) => {
|
|
539
|
+
let value
|
|
540
|
+
let initialLabel = modelValue ?? false
|
|
541
|
+
if (typeof modelValue === 'boolean') {
|
|
542
|
+
value = ''
|
|
543
|
+
} else if (Array.isArray(modelValue)) {
|
|
544
|
+
value = modelValue
|
|
545
|
+
} else if (typeof modelValue === 'number' && props.suggestions.length && typeof props.suggestions[0] === 'object') {
|
|
546
|
+
const suggestionsValue = getObjectFromSuggestions(modelValue)
|
|
547
|
+
value = suggestionsValue[props.valueKey]
|
|
548
|
+
initialLabel = suggestionsValue[props.labelKey]
|
|
549
|
+
} else if (modelValue && typeof modelValue === 'object') {
|
|
550
|
+
if (!Object.keys(modelValue).length) { return {} }
|
|
551
|
+
if (!modelValue[props.valueKey] && modelValue[props.valueKey] !== '') {
|
|
552
|
+
const instance = getCurrentInstance()
|
|
553
|
+
console.error(`Componente '${instance.type.componentName}' option doesn't have a valueKey '${props.valueKey}' key`)
|
|
554
|
+
} else {
|
|
555
|
+
// Se mandou a label no objeto modelValue
|
|
556
|
+
if (modelValue[props.labelKey]) {
|
|
557
|
+
initialLabel = modelValue[props.labelKey]
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
value = modelValue[props.valueKey]
|
|
561
|
+
}
|
|
562
|
+
} else {
|
|
563
|
+
value = modelValue
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return { value, initialLabel }
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* @tagplus
|
|
571
|
+
* Sobrescrito para permitir model com objeto, além de mostrar a label corretamente
|
|
572
|
+
* @param {*} val
|
|
573
|
+
*/
|
|
574
|
+
const getOption = (val) => {
|
|
575
|
+
const { value, initialLabel } = getValueLabelForOption(val)
|
|
576
|
+
|
|
577
|
+
let option
|
|
578
|
+
if (value && initialLabel) {
|
|
579
|
+
option = {
|
|
580
|
+
value,
|
|
581
|
+
currentLabel: initialLabel
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const isObjectValue = toRawType(value).toLowerCase() === 'object'
|
|
586
|
+
const isNull = toRawType(value).toLowerCase() === 'null'
|
|
587
|
+
const isUndefined = toRawType(value).toLowerCase() === 'undefined'
|
|
588
|
+
for (let i = states.cachedOptions.size - 1; i >= 0; i--) {
|
|
589
|
+
const cachedOption = cachedOptionsArray.value[i]
|
|
590
|
+
const isEqualValue = isObjectValue
|
|
591
|
+
? get(cachedOption.value, props.valueKey) === get(value, props.valueKey)
|
|
592
|
+
: cachedOption.value === value
|
|
593
|
+
if (isEqualValue) {
|
|
594
|
+
option = {
|
|
595
|
+
value,
|
|
596
|
+
currentLabel: cachedOption.currentLabel,
|
|
597
|
+
isDisabled: cachedOption.isDisabled,
|
|
598
|
+
}
|
|
599
|
+
break
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
if (option) return option
|
|
603
|
+
const label = isObjectValue ? value.label : !isNull && !isUndefined ? value : ''
|
|
604
|
+
|
|
605
|
+
const newOption = {
|
|
606
|
+
value,
|
|
607
|
+
currentLabel: initialLabel || label,
|
|
608
|
+
}
|
|
609
|
+
return newOption
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const updateHoveringIndex = () => {
|
|
613
|
+
if (!props.multiple) {
|
|
614
|
+
states.hoveringIndex = optionsArray.value.findIndex((item) => {
|
|
615
|
+
return getValueKey(item) === getValueKey(states.selected)
|
|
616
|
+
})
|
|
617
|
+
} else {
|
|
618
|
+
if (states.selected.length > 0) {
|
|
619
|
+
states.hoveringIndex = Math.min(
|
|
620
|
+
...states.selected.map((selected) => {
|
|
621
|
+
return optionsArray.value.findIndex((item) => {
|
|
622
|
+
return getValueKey(item) === getValueKey(selected)
|
|
623
|
+
})
|
|
624
|
+
})
|
|
625
|
+
)
|
|
626
|
+
} else {
|
|
627
|
+
states.hoveringIndex = -1
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const resetSelectionWidth = () => {
|
|
633
|
+
states.selectionWidth = selectionRef.value.getBoundingClientRect().width
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const resetCalculatorWidth = () => {
|
|
637
|
+
states.calculatorWidth = calculatorRef.value.getBoundingClientRect().width
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const resetCollapseItemWidth = () => {
|
|
641
|
+
states.collapseItemWidth =
|
|
642
|
+
collapseItemRef.value.getBoundingClientRect().width
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const updateTooltip = () => {
|
|
646
|
+
tooltipRef.value?.updatePopper?.()
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const onInputChange = () => {
|
|
650
|
+
if (states.inputValue.length > 0 && !expanded.value) {
|
|
651
|
+
expanded.value = true
|
|
652
|
+
}
|
|
653
|
+
handleQueryChange(states.inputValue)
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* @tagplus
|
|
658
|
+
* Sobrescrito porque é sempre remote
|
|
659
|
+
* @param {*} value
|
|
660
|
+
*/
|
|
661
|
+
const onInput = (event) => {
|
|
662
|
+
states.inputValue = event.target.value
|
|
663
|
+
debouncedOnInputChange()
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const debouncedOnInputChange = lodashDebounce(() => {
|
|
667
|
+
onInputChange()
|
|
668
|
+
// eslint-disable-next-line vue/no-ref-object-destructure
|
|
669
|
+
}, debounce.value)
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* @tagplus
|
|
673
|
+
* Sobrescrito para funcionar com model objeto e melhoria caso lista não tenha campos necessários
|
|
674
|
+
* @param {*} val
|
|
675
|
+
*/
|
|
676
|
+
const emitChange = (val) => {
|
|
677
|
+
if (!isEqual(formattedValue, val)) {
|
|
678
|
+
emit(CHANGE_EVENT, val)
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const getLastNotDisabledIndex = (value) =>
|
|
683
|
+
findLastIndex(value, (it) => !states.disabledOptions.has(it))
|
|
684
|
+
|
|
685
|
+
const deletePrevTag = (e) => {
|
|
686
|
+
if (!props.multiple) return
|
|
687
|
+
if (e.code === EVENT_CODE.delete) return
|
|
688
|
+
if (e.target.value.length <= 0) {
|
|
689
|
+
const value = props.modelValue.slice()
|
|
690
|
+
const lastNotDisabledIndex = getLastNotDisabledIndex(value)
|
|
691
|
+
if (lastNotDisabledIndex < 0) return
|
|
692
|
+
value.splice(lastNotDisabledIndex, 1)
|
|
693
|
+
emit(UPDATE_MODEL_EVENT, value)
|
|
694
|
+
emitChange(value)
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const deleteTag = (event, tag) => {
|
|
699
|
+
const index = states.selected.indexOf(tag)
|
|
700
|
+
if (index > -1 && !selectDisabled.value) {
|
|
701
|
+
const value = props.modelValue.slice()
|
|
702
|
+
value.splice(index, 1)
|
|
703
|
+
emit(UPDATE_MODEL_EVENT, value)
|
|
704
|
+
emitChange(value)
|
|
705
|
+
emit('remove-tag', tag.value)
|
|
706
|
+
}
|
|
707
|
+
event.stopPropagation()
|
|
708
|
+
focus()
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
const deleteSelected = (event) => {
|
|
712
|
+
event.stopPropagation()
|
|
713
|
+
const value = props.multiple ? [] : ''
|
|
714
|
+
if (!isString(value)) {
|
|
715
|
+
for (const item of states.selected) {
|
|
716
|
+
if (item.isDisabled) value.push(item.value)
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
emit(UPDATE_MODEL_EVENT, value)
|
|
720
|
+
emitChange(value)
|
|
721
|
+
states.hoveringIndex = -1
|
|
722
|
+
expanded.value = false
|
|
723
|
+
emit('clear')
|
|
724
|
+
focus()
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* @tagplus
|
|
729
|
+
* Criado para emitir valores de acordo com a configuração da prop legacyModel,
|
|
730
|
+
* se recebeu um objeto ou array de objetos e legacyModel é true retornará o valor de valueKey ou array com valueKeys
|
|
731
|
+
* se recebeu um number/string ele será usado como valueKey para encontrar o objeto em suggestions ou retornar um objeto novo
|
|
732
|
+
* @param {String|Number|Object} value
|
|
733
|
+
* @returns {Object}
|
|
734
|
+
*/
|
|
735
|
+
const normalizarValor = (value) => {
|
|
736
|
+
if (isObject(value) && props.legacyModel){
|
|
737
|
+
return value[props.valueKey]
|
|
738
|
+
} else if (!isObject(value) && !props.legacyModel){
|
|
739
|
+
return getObjectFromSuggestions(value)
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
return value
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* @tagplus
|
|
747
|
+
* Criado para emitir valores de acordo com a configuração da prop legacyModel
|
|
748
|
+
* @see normalizarValor
|
|
749
|
+
* @param {String|Number|Object} value
|
|
750
|
+
* @returns {Array|String|Number|Object}
|
|
751
|
+
*/
|
|
752
|
+
const normalizarTipos = (value) => {
|
|
753
|
+
if (props.multiple){
|
|
754
|
+
return value.map(item => {
|
|
755
|
+
return normalizarValor(item)
|
|
756
|
+
})
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
return normalizarValor(value)
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* @tagplus
|
|
764
|
+
* Sobrescrito para corrigir a label no click
|
|
765
|
+
* @param {Object} option
|
|
766
|
+
*/
|
|
767
|
+
const handleOptionSelect = (option) => {
|
|
768
|
+
if (props.multiple) {
|
|
769
|
+
const value = (props.modelValue || []).slice()
|
|
770
|
+
const optionIndex = getValueIndex(value, option.value)
|
|
771
|
+
if (optionIndex > -1) {
|
|
772
|
+
value.splice(optionIndex, 1)
|
|
773
|
+
} else if (props.multipleLimit <= 0 || value.length < props.multipleLimit) {
|
|
774
|
+
const emitValue = props.legacyModel ? option.value : option.item
|
|
775
|
+
value.push(emitValue)
|
|
776
|
+
}
|
|
777
|
+
// Configura o retorno do emit para objeto ou idValue
|
|
778
|
+
const formattedValue = normalizarTipos(value)
|
|
779
|
+
emit(UPDATE_MODEL_EVENT, formattedValue)
|
|
780
|
+
emitChange(formattedValue)
|
|
781
|
+
if (option.created) {
|
|
782
|
+
handleQueryChange('')
|
|
783
|
+
}
|
|
784
|
+
if (!props.reserveKeyword) {
|
|
785
|
+
states.inputValue = ''
|
|
786
|
+
}
|
|
787
|
+
} else {
|
|
788
|
+
// Configura o retorno do emit para objeto ou idValue
|
|
789
|
+
const emitValue = props.legacyModel ? option.value : option.item
|
|
790
|
+
const formattedValue = normalizarTipos(emitValue)
|
|
791
|
+
emit(UPDATE_MODEL_EVENT, formattedValue)
|
|
792
|
+
emitChange(formattedValue)
|
|
793
|
+
expanded.value = false
|
|
794
|
+
}
|
|
795
|
+
focus()
|
|
796
|
+
if (expanded.value) return
|
|
797
|
+
nextTick(() => {
|
|
798
|
+
scrollToOption(option)
|
|
799
|
+
})
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* @tagplus
|
|
804
|
+
* Sobrescrito para conseguir comparar itens tipo objeto dentro do array
|
|
805
|
+
* @param {Array} arr
|
|
806
|
+
* @param {*} value
|
|
807
|
+
* @return {Number}
|
|
808
|
+
*/
|
|
809
|
+
const getValueIndex = (arr = [], value) => {
|
|
810
|
+
const compareValue = isObject(value) ? value[props.valueKey]: value
|
|
811
|
+
for (let i = 0; i < arr.length; i++) {
|
|
812
|
+
const el = arr[i]
|
|
813
|
+
const elCompareValue = isObject(el) ? el[props.valueKey] : el
|
|
814
|
+
if (compareValue === elCompareValue) {
|
|
815
|
+
return i
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
return -1
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const scrollToOption = (option) => {
|
|
823
|
+
const targetOption = isArray(option) ? option[0] : option
|
|
824
|
+
let target = null
|
|
825
|
+
|
|
826
|
+
if (targetOption?.value) {
|
|
827
|
+
const options = optionsArray.value.filter(
|
|
828
|
+
(item) => item.value === targetOption.value
|
|
829
|
+
)
|
|
830
|
+
if (options.length > 0) {
|
|
831
|
+
target = options[0].$el
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
if (tooltipRef.value && target) {
|
|
836
|
+
const menu = tooltipRef.value?.popperRef?.contentRef?.querySelector?.(
|
|
837
|
+
`.${nsSelect.be('dropdown', 'wrap')}`
|
|
838
|
+
)
|
|
839
|
+
if (menu) {
|
|
840
|
+
scrollIntoView(menu, target)
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
scrollbarRef.value?.handleScroll()
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
const onOptionCreate = (vm) => {
|
|
847
|
+
states.options.set(vm.value, vm)
|
|
848
|
+
states.cachedOptions.set(vm.value, vm)
|
|
849
|
+
vm.disabled && states.disabledOptions.set(vm.value, vm)
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
const onOptionDestroy = (key, vm) => {
|
|
853
|
+
if (states.options.get(key) === vm) {
|
|
854
|
+
states.options.delete(key)
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
const {
|
|
859
|
+
handleCompositionStart,
|
|
860
|
+
handleCompositionUpdate,
|
|
861
|
+
handleCompositionEnd,
|
|
862
|
+
} = useInput((e) => onInput(e))
|
|
863
|
+
|
|
864
|
+
const popperRef = computed(() => {
|
|
865
|
+
return tooltipRef.value?.popperRef?.contentRef
|
|
866
|
+
})
|
|
867
|
+
|
|
868
|
+
const handleMenuEnter = () => {
|
|
869
|
+
nextTick(() => scrollToOption(states.selected))
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
const focus = () => {
|
|
873
|
+
inputRef.value?.focus()
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
const blur = () => {
|
|
877
|
+
handleClickOutside()
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
const handleClearClick = (event) => {
|
|
881
|
+
deleteSelected(event)
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
const handleClickOutside = (event) => {
|
|
885
|
+
expanded.value = false
|
|
886
|
+
|
|
887
|
+
if (isFocused.value) {
|
|
888
|
+
const _event = new FocusEvent('focus', event)
|
|
889
|
+
nextTick(() => handleBlur(_event))
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
const handleEsc = () => {
|
|
894
|
+
if (states.inputValue.length > 0) {
|
|
895
|
+
states.inputValue = ''
|
|
896
|
+
} else {
|
|
897
|
+
expanded.value = false
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const toggleMenu = () => {
|
|
902
|
+
if (selectDisabled.value) return
|
|
903
|
+
|
|
904
|
+
if (states.menuVisibleOnFocus) {
|
|
905
|
+
// controlled by automaticDropdown
|
|
906
|
+
states.menuVisibleOnFocus = false
|
|
907
|
+
} else {
|
|
908
|
+
expanded.value = !expanded.value
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
const selectOption = () => {
|
|
913
|
+
if (!expanded.value) {
|
|
914
|
+
toggleMenu()
|
|
915
|
+
} else {
|
|
916
|
+
if (optionsArray.value[states.hoveringIndex]) {
|
|
917
|
+
handleOptionSelect(optionsArray.value[states.hoveringIndex])
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
const getValueKey = (item) => {
|
|
923
|
+
return isObject(item.value) ? get(item.value, props.valueKey) : item.value
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
const optionsAllDisabled = computed(() =>
|
|
927
|
+
optionsArray.value
|
|
928
|
+
.filter((option) => option.visible)
|
|
929
|
+
.every((option) => option.disabled)
|
|
930
|
+
)
|
|
931
|
+
|
|
932
|
+
const showTagList = computed(() => {
|
|
933
|
+
if (!props.multiple) {
|
|
934
|
+
return []
|
|
935
|
+
}
|
|
936
|
+
return props.collapseTags
|
|
937
|
+
? states.selected.slice(0, props.maxCollapseTags)
|
|
938
|
+
: states.selected
|
|
939
|
+
})
|
|
940
|
+
|
|
941
|
+
const collapseTagList = computed(() => {
|
|
942
|
+
if (!props.multiple) {
|
|
943
|
+
return []
|
|
944
|
+
}
|
|
945
|
+
return props.collapseTags
|
|
946
|
+
? states.selected.slice(props.maxCollapseTags)
|
|
947
|
+
: []
|
|
948
|
+
})
|
|
949
|
+
|
|
950
|
+
const navigateOptions = (direction) => {
|
|
951
|
+
if (!expanded.value) {
|
|
952
|
+
expanded.value = true
|
|
953
|
+
return
|
|
954
|
+
}
|
|
955
|
+
if (states.options.size === 0 || filteredOptionsCount.value === 0) return
|
|
956
|
+
|
|
957
|
+
if (!optionsAllDisabled.value) {
|
|
958
|
+
if (direction === 'next') {
|
|
959
|
+
states.hoveringIndex++
|
|
960
|
+
if (states.hoveringIndex === states.options.size) {
|
|
961
|
+
states.hoveringIndex = 0
|
|
962
|
+
}
|
|
963
|
+
} else if (direction === 'prev') {
|
|
964
|
+
states.hoveringIndex--
|
|
965
|
+
if (states.hoveringIndex < 0) {
|
|
966
|
+
states.hoveringIndex = states.options.size - 1
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
const option = optionsArray.value[states.hoveringIndex]
|
|
970
|
+
if (
|
|
971
|
+
option.disabled === true ||
|
|
972
|
+
option.states.groupDisabled === true ||
|
|
973
|
+
!option.visible
|
|
974
|
+
) {
|
|
975
|
+
navigateOptions(direction)
|
|
976
|
+
}
|
|
977
|
+
nextTick(() => scrollToOption(hoverOption.value))
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
const getGapWidth = () => {
|
|
982
|
+
if (!selectionRef.value) return 0
|
|
983
|
+
const style = window.getComputedStyle(selectionRef.value)
|
|
984
|
+
return Number.parseFloat(style.gap || '6px')
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// computed style
|
|
988
|
+
const tagStyle = computed(() => {
|
|
989
|
+
const gapWidth = getGapWidth()
|
|
990
|
+
const maxWidth =
|
|
991
|
+
collapseItemRef.value && props.maxCollapseTags === 1
|
|
992
|
+
? states.selectionWidth - states.collapseItemWidth - gapWidth
|
|
993
|
+
: states.selectionWidth
|
|
994
|
+
return { maxWidth: `${maxWidth}px` }
|
|
995
|
+
})
|
|
996
|
+
|
|
997
|
+
const collapseTagStyle = computed(() => {
|
|
998
|
+
return { maxWidth: `${states.selectionWidth}px` }
|
|
999
|
+
})
|
|
1000
|
+
|
|
1001
|
+
const inputStyle = computed(() => ({
|
|
1002
|
+
width: `${Math.max(states.calculatorWidth, MINIMUM_INPUT_WIDTH)}px`,
|
|
1003
|
+
}))
|
|
1004
|
+
|
|
1005
|
+
if (props.multiple && !isArray(props.modelValue)) {
|
|
1006
|
+
emit(UPDATE_MODEL_EVENT, [])
|
|
1007
|
+
}
|
|
1008
|
+
if (!props.multiple && isArray(props.modelValue)) {
|
|
1009
|
+
emit(UPDATE_MODEL_EVENT, '')
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/**
|
|
1013
|
+
* @tagplus
|
|
1014
|
+
* Criado para apagar os itens de forma simplificada
|
|
1015
|
+
*/
|
|
1016
|
+
const clearTags = () => {
|
|
1017
|
+
states.selectedLabel = ''
|
|
1018
|
+
// Faz uma requisição limpa
|
|
1019
|
+
states.doRequest = true
|
|
1020
|
+
states.previousQuery = false
|
|
1021
|
+
handleQueryChange('')
|
|
1022
|
+
|
|
1023
|
+
// Eventos ao limpar
|
|
1024
|
+
emit(UPDATE_MODEL_EVENT, null)
|
|
1025
|
+
|
|
1026
|
+
if (typeof props.modelValue === 'undefined') {
|
|
1027
|
+
emit('change', null, null)
|
|
1028
|
+
} else {
|
|
1029
|
+
emit('change', null, props.modelValue)
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
useResizeObserver(selectionRef, resetSelectionWidth)
|
|
1034
|
+
useResizeObserver(calculatorRef, resetCalculatorWidth)
|
|
1035
|
+
useResizeObserver(menuRef, updateTooltip)
|
|
1036
|
+
useResizeObserver(wrapperRef, updateTooltip)
|
|
1037
|
+
useResizeObserver(collapseItemRef, resetCollapseItemWidth)
|
|
1038
|
+
|
|
1039
|
+
onMounted(() => {
|
|
1040
|
+
// INICIO @tagplus
|
|
1041
|
+
states.doRequest = false
|
|
1042
|
+
|
|
1043
|
+
if (props.loadOnCreate) {
|
|
1044
|
+
states.previousQuery = false
|
|
1045
|
+
// Chama função do element-ui select que faz o remote method
|
|
1046
|
+
handleQueryChange('')
|
|
1047
|
+
} else {
|
|
1048
|
+
// Marca para fazer a requisição no primeiro clique
|
|
1049
|
+
states.doRequest = true
|
|
1050
|
+
}
|
|
1051
|
+
// FIM @tagplus
|
|
1052
|
+
|
|
1053
|
+
setSelected()
|
|
1054
|
+
})
|
|
1055
|
+
|
|
1056
|
+
return {
|
|
1057
|
+
inputId,
|
|
1058
|
+
contentId,
|
|
1059
|
+
nsSelect,
|
|
1060
|
+
nsInput,
|
|
1061
|
+
states,
|
|
1062
|
+
isFocused,
|
|
1063
|
+
expanded,
|
|
1064
|
+
optionsArray,
|
|
1065
|
+
hoverOption,
|
|
1066
|
+
selectSize,
|
|
1067
|
+
filteredOptionsCount,
|
|
1068
|
+
resetCalculatorWidth,
|
|
1069
|
+
updateTooltip,
|
|
1070
|
+
debouncedOnInputChange,
|
|
1071
|
+
onInput,
|
|
1072
|
+
deletePrevTag,
|
|
1073
|
+
deleteTag,
|
|
1074
|
+
deleteSelected,
|
|
1075
|
+
handleOptionSelect,
|
|
1076
|
+
scrollToOption,
|
|
1077
|
+
hasModelValue,
|
|
1078
|
+
shouldShowPlaceholder,
|
|
1079
|
+
currentPlaceholder,
|
|
1080
|
+
showClose,
|
|
1081
|
+
iconReverse,
|
|
1082
|
+
validateState,
|
|
1083
|
+
validateIcon,
|
|
1084
|
+
showNewOption,
|
|
1085
|
+
updateOptions,
|
|
1086
|
+
collapseTagSize,
|
|
1087
|
+
setSelected,
|
|
1088
|
+
selectDisabled,
|
|
1089
|
+
emptyText,
|
|
1090
|
+
handleCompositionStart,
|
|
1091
|
+
handleCompositionUpdate,
|
|
1092
|
+
handleCompositionEnd,
|
|
1093
|
+
onOptionCreate,
|
|
1094
|
+
onOptionDestroy,
|
|
1095
|
+
handleMenuEnter,
|
|
1096
|
+
handleFocus,
|
|
1097
|
+
focus,
|
|
1098
|
+
blur,
|
|
1099
|
+
handleBlur,
|
|
1100
|
+
handleClearClick,
|
|
1101
|
+
handleClickOutside,
|
|
1102
|
+
handleEsc,
|
|
1103
|
+
toggleMenu,
|
|
1104
|
+
selectOption,
|
|
1105
|
+
getValueKey,
|
|
1106
|
+
navigateOptions,
|
|
1107
|
+
dropdownMenuVisible,
|
|
1108
|
+
showTagList,
|
|
1109
|
+
collapseTagList,
|
|
1110
|
+
clearTags,
|
|
1111
|
+
suggestionsList,
|
|
1112
|
+
newItem,
|
|
1113
|
+
myId,
|
|
1114
|
+
|
|
1115
|
+
// computed style
|
|
1116
|
+
tagStyle,
|
|
1117
|
+
collapseTagStyle,
|
|
1118
|
+
inputStyle,
|
|
1119
|
+
|
|
1120
|
+
// DOM ref
|
|
1121
|
+
popperRef,
|
|
1122
|
+
inputRef,
|
|
1123
|
+
tooltipRef,
|
|
1124
|
+
calculatorRef,
|
|
1125
|
+
suffixRef,
|
|
1126
|
+
selectRef,
|
|
1127
|
+
wrapperRef,
|
|
1128
|
+
selectionRef,
|
|
1129
|
+
scrollbarRef,
|
|
1130
|
+
menuRef,
|
|
1131
|
+
collapseItemRef,
|
|
1132
|
+
}
|
|
1133
|
+
}
|