mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
fix(components): [input-tag] paste multiple delimiters (#21256)
* fix(components): [input-tag] paste multiple delimiters * chore: refact * refactor: easier to read & import types * chore: remove useless convert regex Co-authored-by: btea <2356281422@qq.com> * chore: fix * chore: fix * chore: same logic on paste & better tests * chore: fix edge case * chore: fix * chore: add --------- Co-authored-by: btea <2356281422@qq.com>
This commit is contained in:
@@ -137,15 +137,15 @@ input-tag/prefix-suffix
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Description | Type |
|
||||
| ---------- | --------------------------------------- | ---------------------------------------- |
|
||||
| change | triggers when the modelValue change | ^[Function]`(value: string[]) => void` |
|
||||
| input | triggers when the input value change | ^[Function]`(value: string) => void` |
|
||||
| add-tag | triggers when a tag is added | ^[Function]`(value: string) => void` |
|
||||
| remove-tag | triggers when a tag is removed | ^[Function]`(value: string) => void` |
|
||||
| focus | triggers when InputTag focuses | ^[Function]`(event: FocusEvent) => void` |
|
||||
| blur | triggers when InputTag blurs | ^[Function]`(event: FocusEvent) => void` |
|
||||
| clear | triggers when the clear icon is clicked | ^[Function]`() => void` |
|
||||
| Name | Description | Type |
|
||||
| ---------- | --------------------------------------- | ------------------------------------------------- |
|
||||
| change | triggers when the modelValue change | ^[Function]`(value: string[]) => void` |
|
||||
| input | triggers when the input value change | ^[Function]`(value: string) => void` |
|
||||
| add-tag | triggers when a tag is added | ^[Function]`(value: string \| string []) => void` |
|
||||
| remove-tag | triggers when a tag is removed | ^[Function]`(value: string) => void` |
|
||||
| focus | triggers when InputTag focuses | ^[Function]`(event: FocusEvent) => void` |
|
||||
| blur | triggers when InputTag blurs | ^[Function]`(event: FocusEvent) => void` |
|
||||
| clear | triggers when the clear icon is clicked | ^[Function]`() => void` |
|
||||
|
||||
### Slots
|
||||
|
||||
|
||||
@@ -233,26 +233,57 @@ describe('InputTag.vue', () => {
|
||||
describe('delimiter', () => {
|
||||
test('with string', async () => {
|
||||
const inputValue = ref<string[]>()
|
||||
const addTag = vi.fn()
|
||||
const wrapper = mount(() => (
|
||||
<InputTag v-model={inputValue.value} delimiter="," />
|
||||
<InputTag v-model={inputValue.value} delimiter="," onAdd-tag={addTag} />
|
||||
))
|
||||
|
||||
await wrapper.find('input').setValue(`${AXIOM},`)
|
||||
|
||||
expect(addTag).toBeCalledWith(AXIOM)
|
||||
expect(wrapper.findAll('.el-tag').length).toBe(1)
|
||||
expect(wrapper.find('.el-tag').text()).toBe(AXIOM)
|
||||
expect(inputValue.value).toEqual([AXIOM])
|
||||
})
|
||||
test('with RegExp', async () => {
|
||||
const inputValue = ref<string[]>()
|
||||
const addTag = vi.fn()
|
||||
const wrapper = mount(() => (
|
||||
<InputTag v-model={inputValue.value} delimiter={/\./} />
|
||||
<InputTag
|
||||
v-model={inputValue.value}
|
||||
delimiter={/\./}
|
||||
onAdd-tag={addTag}
|
||||
/>
|
||||
))
|
||||
|
||||
await wrapper.find('input').setValue(`${AXIOM}.`)
|
||||
|
||||
expect(addTag).toBeCalledWith(AXIOM)
|
||||
expect(wrapper.findAll('.el-tag').length).toBe(1)
|
||||
expect(wrapper.find('.el-tag').text()).toBe(AXIOM)
|
||||
expect(inputValue.value).toEqual([AXIOM])
|
||||
})
|
||||
test('paste multiple delimiter', async () => {
|
||||
const inputValue = ref<string[]>()
|
||||
const addTag = vi.fn()
|
||||
const wrapper = mount(() => (
|
||||
<InputTag
|
||||
v-model={inputValue.value}
|
||||
delimiter={/\./}
|
||||
onAdd-tag={addTag}
|
||||
/>
|
||||
))
|
||||
|
||||
await wrapper
|
||||
.find('input')
|
||||
.setValue(`${AXIOM}.${AXIOM}.${AXIOM}.${AXIOM}.`)
|
||||
|
||||
const result = [AXIOM, AXIOM, AXIOM, AXIOM]
|
||||
expect(wrapper.findAll('.el-tag').length).toBe(4)
|
||||
expect(addTag).toBeCalledWith(result)
|
||||
wrapper
|
||||
.findAll('.el-tag')
|
||||
.forEach((tag) => expect(tag.text()).toBe(AXIOM))
|
||||
expect(inputValue.value).toEqual(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('events', () => {
|
||||
|
||||
@@ -5,14 +5,12 @@ import {
|
||||
INPUT_EVENT,
|
||||
UPDATE_MODEL_EVENT,
|
||||
} from '@element-plus/constants'
|
||||
import { type EmitFn, debugWarn, isUndefined } from '@element-plus/utils'
|
||||
import { debugWarn, ensureArray, isUndefined } from '@element-plus/utils'
|
||||
import { useComposition, useFocusController } from '@element-plus/hooks'
|
||||
import {
|
||||
type FormItemContext,
|
||||
useFormDisabled,
|
||||
useFormSize,
|
||||
} from '@element-plus/components/form'
|
||||
import { useFormDisabled, useFormSize } from '@element-plus/components/form'
|
||||
|
||||
import type { EmitFn } from '@element-plus/utils'
|
||||
import type { FormItemContext } from '@element-plus/components/form'
|
||||
import type { InputTagEmits, InputTagProps } from '../input-tag'
|
||||
|
||||
interface UseInputTagOptions {
|
||||
@@ -41,6 +39,26 @@ export function useInputTag({ props, emit, formItem }: UseInputTagOptions) {
|
||||
: (props.modelValue?.length ?? 0) >= props.max
|
||||
})
|
||||
|
||||
const addTagsEmit = (value: string | string[]) => {
|
||||
const list = [...(props.modelValue ?? []), ...ensureArray(value)]
|
||||
|
||||
emit(UPDATE_MODEL_EVENT, list)
|
||||
emit(CHANGE_EVENT, list)
|
||||
emit('add-tag', value)
|
||||
inputValue.value = undefined
|
||||
}
|
||||
|
||||
const getDelimitedTags = (input: string) => {
|
||||
const tags = input
|
||||
.split(props.delimiter)
|
||||
.filter((val) => val && val !== input)
|
||||
if (props.max) {
|
||||
const maxInsert = props.max - (props.modelValue?.length ?? 0)
|
||||
tags.splice(maxInsert)
|
||||
}
|
||||
return tags.length === 1 ? tags[0] : tags
|
||||
}
|
||||
|
||||
const handleInput = (event: Event) => {
|
||||
if (inputLimit.value) {
|
||||
inputValue.value = undefined
|
||||
@@ -48,11 +66,10 @@ export function useInputTag({ props, emit, formItem }: UseInputTagOptions) {
|
||||
}
|
||||
|
||||
if (isComposing.value) return
|
||||
if (props.delimiter) {
|
||||
const replacement = inputValue.value?.replace(props.delimiter, '')
|
||||
if (replacement?.length !== inputValue.value?.length) {
|
||||
inputValue.value = replacement
|
||||
handleAddTag()
|
||||
if (props.delimiter && inputValue.value) {
|
||||
const tags = getDelimitedTags(inputValue.value)
|
||||
if (tags.length) {
|
||||
addTagsEmit(tags)
|
||||
}
|
||||
}
|
||||
emit(INPUT_EVENT, (event.target as HTMLInputElement).value)
|
||||
@@ -86,12 +103,7 @@ export function useInputTag({ props, emit, formItem }: UseInputTagOptions) {
|
||||
const handleAddTag = () => {
|
||||
const value = inputValue.value?.trim()
|
||||
if (!value || inputLimit.value) return
|
||||
const list = [...(props.modelValue ?? []), value]
|
||||
|
||||
emit(UPDATE_MODEL_EVENT, list)
|
||||
emit(CHANGE_EVENT, list)
|
||||
emit('add-tag', value)
|
||||
inputValue.value = undefined
|
||||
addTagsEmit(value)
|
||||
}
|
||||
|
||||
const handleRemoveTag = (index: number) => {
|
||||
|
||||
@@ -140,7 +140,7 @@ export const inputTagEmits = {
|
||||
isArray(value) || isUndefined(value),
|
||||
[CHANGE_EVENT]: (value?: string[]) => isArray(value) || isUndefined(value),
|
||||
[INPUT_EVENT]: (value: string) => isString(value),
|
||||
'add-tag': (value: string) => isString(value),
|
||||
'add-tag': (value: string | string[]) => isString(value) || isArray(value),
|
||||
'remove-tag': (value: string) => isString(value),
|
||||
focus: (evt: FocusEvent) => evt instanceof FocusEvent,
|
||||
blur: (evt: FocusEvent) => evt instanceof FocusEvent,
|
||||
|
||||
Reference in New Issue
Block a user