Skip to content

Commit 7c53fa0

Browse files
authored
feat(VCombobox): create new items when pasting with line breaks (#22304)
fixes #10813
1 parent 55f8b52 commit 7c53fa0

File tree

2 files changed

+42
-10
lines changed

2 files changed

+42
-10
lines changed

packages/vuetify/src/components/VCombobox/VCombobox.tsx

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,16 +165,9 @@ export const VCombobox = genericComponent<new <
165165
}
166166

167167
if (val && props.multiple && props.delimiters?.length) {
168-
const signsToMatch = props.delimiters.map(escapeForRegex).join('|')
169-
const values = val.split(new RegExp(`(?:${signsToMatch})+`))
168+
const values = splitByDelimiters(val)
170169
if (values.length > 1) {
171-
for (let v of values) {
172-
v = v.trim()
173-
if (v) {
174-
select(transformItem(props, v))
175-
await nextTick()
176-
}
177-
}
170+
selectMultiple(values)
178171
_search.value = ''
179172
}
180173
}
@@ -364,6 +357,15 @@ export const VCombobox = genericComponent<new <
364357
selectionIndex.value = -1
365358
}
366359
}
360+
function onPaste (e: ClipboardEvent) {
361+
const clipboardText = e?.clipboardData?.getData('Text') ?? ''
362+
const values = splitByDelimiters(clipboardText)
363+
364+
if (values.length > 1 && props.multiple) {
365+
e.preventDefault()
366+
selectMultiple(values)
367+
}
368+
}
367369
function onAfterEnter () {
368370
if (props.eager) {
369371
vVirtualScrollRef.value?.calculateVisibleItems()
@@ -410,7 +412,20 @@ export const VCombobox = genericComponent<new <
410412
})
411413
}
412414
}
413-
415+
function splitByDelimiters (val: string) {
416+
const effectiveDelimiters = ['\n', ...props.delimiters ?? []]
417+
const signsToMatch = effectiveDelimiters.map(escapeForRegex).join('|')
418+
return val.split(new RegExp(`(?:${signsToMatch})+`))
419+
}
420+
async function selectMultiple (values: string[]) {
421+
for (let value of values) {
422+
value = value.trim()
423+
if (value) {
424+
select(transformItem(props, value))
425+
await nextTick()
426+
}
427+
}
428+
}
414429
function onFocusin (e: FocusEvent) {
415430
isFocused.value = true
416431
setTimeout(() => {
@@ -500,6 +515,7 @@ export const VCombobox = genericComponent<new <
500515
onClick:clear={ onClear }
501516
onMousedown:control={ onMousedownControl }
502517
onKeydown={ onKeydown }
518+
onPaste={ onPaste }
503519
aria-expanded={ ariaExpanded.value }
504520
aria-controls={ ariaControls.value }
505521
>

packages/vuetify/src/components/VCombobox/__tests__/VCombobox.spec.browser.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,22 @@ describe('VCombobox', () => {
802802
await expect.poll(() => screen.queryAllByRole('option')).toHaveLength(1)
803803
})
804804

805+
it('should create new items when pasting with line break characters', async () => {
806+
const model = ref(null)
807+
render(() => (
808+
<VCombobox
809+
v-model={ model.value }
810+
multiple
811+
delimiters={[',']}
812+
/>
813+
))
814+
815+
await userEvent.tab()
816+
navigator.clipboard.writeText('foo,\nbar')
817+
await userEvent.paste()
818+
expect(model.value).toEqual(['foo', 'bar'])
819+
})
820+
805821
describe('Showcase', () => {
806822
generate({ stories })
807823
})

0 commit comments

Comments
 (0)