From 15df05894ffebd698d0ef0e04bff107bdf2424c9 Mon Sep 17 00:00:00 2001 From: Ali Maher Date: Thu, 23 Apr 2026 17:27:55 +0200 Subject: [PATCH 1/2] feat(pin-input): add delimiter option to seperate inputs at sepecific points --- docs/content/docs/2.components/pin-input.md | 47 ++ .../nuxt/app/pages/components/pin-input.vue | 1 + src/runtime/components/PinInput.vue | 32 +- .../__snapshots__/PinInput-vue.spec.ts.snap | 519 +++++++++++++++--- .../__snapshots__/PinInput.spec.ts.snap | 519 +++++++++++++++--- 5 files changed, 949 insertions(+), 169 deletions(-) diff --git a/docs/content/docs/2.components/pin-input.md b/docs/content/docs/2.components/pin-input.md index be4dad7c61..9d0bff48e1 100644 --- a/docs/content/docs/2.components/pin-input.md +++ b/docs/content/docs/2.components/pin-input.md @@ -170,12 +170,59 @@ props: --- :: +### Delimiter + +Use the `delimiterPositions` prop to insert delimiters between specific input fields. Pass an array of numbers indicating after which position to insert a delimiter. + +::component-code +--- +ignore: + - placeholder +props: + length: 6 + placeholder: '○' + delimiterPositions: [3] +--- +:: + +You can also insert multiple delimiters by passing an array with multiple positions: + +::component-code +--- +ignore: + - placeholder +props: + length: 9 + placeholder: '○' + delimiterPositions: [3, 6] +--- +:: + +Use the `delimiter` slot to customize the delimiter appearance: + +::component-code +--- +ignore: + - placeholder +props: + length: 6 + placeholder: '○' + delimiterPositions: [3] +slots: + delimiter: '-' +--- +:: + ## API ### Props :component-props +### Slots + +:component-slots + ### Emits :component-emits diff --git a/playgrounds/nuxt/app/pages/components/pin-input.vue b/playgrounds/nuxt/app/pages/components/pin-input.vue index 937ea636e9..f19cf5b81d 100644 --- a/playgrounds/nuxt/app/pages/components/pin-input.vue +++ b/playgrounds/nuxt/app/pages/components/pin-input.vue @@ -28,5 +28,6 @@ const onComplete = (e: string[]) => { + diff --git a/src/runtime/components/PinInput.vue b/src/runtime/components/PinInput.vue index 52a254c82f..ad95aafac0 100644 --- a/src/runtime/components/PinInput.vue +++ b/src/runtime/components/PinInput.vue @@ -39,6 +39,11 @@ export interface PinInputProps extends Pick - + diff --git a/test/components/__snapshots__/PinInput-vue.spec.ts.snap b/test/components/__snapshots__/PinInput-vue.spec.ts.snap index 2464c8299f..6a2546272d 100644 --- a/test/components/__snapshots__/PinInput-vue.spec.ts.snap +++ b/test/components/__snapshots__/PinInput-vue.spec.ts.snap @@ -1,81 +1,442 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`PinInput > renders with ariaLabel correctly 1`] = `"
"`; - -exports[`PinInput > renders with as correctly 1`] = `"
"`; - -exports[`PinInput > renders with class correctly 1`] = `"
"`; - -exports[`PinInput > renders with defaultValue correctly 1`] = `"
"`; - -exports[`PinInput > renders with disabled correctly 1`] = `"
"`; - -exports[`PinInput > renders with id correctly 1`] = `"
"`; - -exports[`PinInput > renders with length correctly 1`] = `"
"`; - -exports[`PinInput > renders with mask correctly 1`] = `"
"`; - -exports[`PinInput > renders with modelValue correctly 1`] = `"
"`; - -exports[`PinInput > renders with name correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant ghost correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant ghost highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant none correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant none highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant outline correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant outline highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant soft correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant soft highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant subtle correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant subtle highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with otp correctly 1`] = `"
"`; - -exports[`PinInput > renders with placeholder correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant ghost correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant ghost highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant none correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant none highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant outline correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant outline highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant soft correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant soft highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant subtle correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant subtle highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with required correctly 1`] = `"
"`; - -exports[`PinInput > renders with size lg correctly 1`] = `"
"`; - -exports[`PinInput > renders with size md correctly 1`] = `"
"`; - -exports[`PinInput > renders with size sm correctly 1`] = `"
"`; - -exports[`PinInput > renders with size xl correctly 1`] = `"
"`; - -exports[`PinInput > renders with size xs correctly 1`] = `"
"`; - -exports[`PinInput > renders with type correctly 1`] = `"
"`; - -exports[`PinInput > renders with ui correctly 1`] = `"
"`; +exports[`PinInput > renders with ariaLabel correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with as correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with class correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with defaultValue correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with disabled correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with id correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with length correctly 1`] = ` +"
+ + + + + + + +
" +`; + +exports[`PinInput > renders with mask correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with modelValue correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with name correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant ghost correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant ghost highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant none correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant none highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant outline correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant outline highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant soft correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant soft highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant subtle correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant subtle highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with otp correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with placeholder correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant ghost correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant ghost highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant none correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant none highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant outline correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant outline highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant soft correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant soft highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant subtle correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant subtle highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with required correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with size lg correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with size md correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with size sm correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with size xl correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with size xs correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with type correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with ui correctly 1`] = ` +"
+ + + + + + +
" +`; diff --git a/test/components/__snapshots__/PinInput.spec.ts.snap b/test/components/__snapshots__/PinInput.spec.ts.snap index 2464c8299f..6a2546272d 100644 --- a/test/components/__snapshots__/PinInput.spec.ts.snap +++ b/test/components/__snapshots__/PinInput.spec.ts.snap @@ -1,81 +1,442 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`PinInput > renders with ariaLabel correctly 1`] = `"
"`; - -exports[`PinInput > renders with as correctly 1`] = `"
"`; - -exports[`PinInput > renders with class correctly 1`] = `"
"`; - -exports[`PinInput > renders with defaultValue correctly 1`] = `"
"`; - -exports[`PinInput > renders with disabled correctly 1`] = `"
"`; - -exports[`PinInput > renders with id correctly 1`] = `"
"`; - -exports[`PinInput > renders with length correctly 1`] = `"
"`; - -exports[`PinInput > renders with mask correctly 1`] = `"
"`; - -exports[`PinInput > renders with modelValue correctly 1`] = `"
"`; - -exports[`PinInput > renders with name correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant ghost correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant ghost highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant none correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant none highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant outline correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant outline highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant soft correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant soft highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant subtle correctly 1`] = `"
"`; - -exports[`PinInput > renders with neutral variant subtle highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with otp correctly 1`] = `"
"`; - -exports[`PinInput > renders with placeholder correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant ghost correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant ghost highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant none correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant none highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant outline correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant outline highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant soft correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant soft highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant subtle correctly 1`] = `"
"`; - -exports[`PinInput > renders with primary variant subtle highlight correctly 1`] = `"
"`; - -exports[`PinInput > renders with required correctly 1`] = `"
"`; - -exports[`PinInput > renders with size lg correctly 1`] = `"
"`; - -exports[`PinInput > renders with size md correctly 1`] = `"
"`; - -exports[`PinInput > renders with size sm correctly 1`] = `"
"`; - -exports[`PinInput > renders with size xl correctly 1`] = `"
"`; - -exports[`PinInput > renders with size xs correctly 1`] = `"
"`; - -exports[`PinInput > renders with type correctly 1`] = `"
"`; - -exports[`PinInput > renders with ui correctly 1`] = `"
"`; +exports[`PinInput > renders with ariaLabel correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with as correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with class correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with defaultValue correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with disabled correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with id correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with length correctly 1`] = ` +"
+ + + + + + + +
" +`; + +exports[`PinInput > renders with mask correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with modelValue correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with name correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant ghost correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant ghost highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant none correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant none highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant outline correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant outline highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant soft correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant soft highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant subtle correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with neutral variant subtle highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with otp correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with placeholder correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant ghost correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant ghost highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant none correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant none highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant outline correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant outline highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant soft correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant soft highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant subtle correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with primary variant subtle highlight correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with required correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with size lg correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with size md correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with size sm correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with size xl correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with size xs correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with type correctly 1`] = ` +"
+ + + + + + +
" +`; + +exports[`PinInput > renders with ui correctly 1`] = ` +"
+ + + + + + +
" +`; From d5d521a2f94a9ef199c2ba9a1304fc07f06b3956 Mon Sep 17 00:00:00 2001 From: Ali Maher Date: Fri, 24 Apr 2026 16:33:35 +0300 Subject: [PATCH 2/2] fix(pin-input): avoid trailing delimiter case & fix slot render and add soon badge in docs --- docs/content/docs/2.components/pin-input.md | 6 ++++-- src/runtime/components/PinInput.vue | 10 +++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/content/docs/2.components/pin-input.md b/docs/content/docs/2.components/pin-input.md index 9d0bff48e1..3410abf444 100644 --- a/docs/content/docs/2.components/pin-input.md +++ b/docs/content/docs/2.components/pin-input.md @@ -170,7 +170,7 @@ props: --- :: -### Delimiter +### Delimiter :badge{label="Soon" class="align-text-top"} Use the `delimiterPositions` prop to insert delimiters between specific input fields. Pass an array of numbers indicating after which position to insert a delimiter. @@ -209,7 +209,9 @@ props: placeholder: '○' delimiterPositions: [3] slots: - delimiter: '-' + delimiter: | + + - --- :: diff --git a/src/runtime/components/PinInput.vue b/src/runtime/components/PinInput.vue index ad95aafac0..6e26ca66ba 100644 --- a/src/runtime/components/PinInput.vue +++ b/src/runtime/components/PinInput.vue @@ -115,6 +115,10 @@ function autoFocus() { } } +function shouldInsertDelimiter(index: number) { + return props.delimiterPositions?.includes((index as number) + 1) && (index as number) + 1 < looseToNumber(props.length) +} + onMounted(() => { setTimeout(() => { autoFocus() @@ -149,7 +153,11 @@ defineExpose({ @blur="onBlur" @focus="emitFormFocus" /> -