Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/runtime/components/InputDate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,51 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.inputDate ||

const inputsRef = ref<ComponentPublicInstance[]>([])

const isComposing = ref(false)

function onCompositionStart() {
isComposing.value = true
}

function onCompositionEnd(event: Event) {
isComposing.value = false
const target = event.target as HTMLInputElement
const data = (event as CompositionEvent).data

if (target && data) {
// Dispatch fake keydown and input events for reka-ui to recognize numeric input during IME composition.
for (const char of data) {
target.dispatchEvent(new KeyboardEvent('keydown', {
key: char,
code: `Digit${char}`,
bubbles: true,
cancelable: true
}))

target.dispatchEvent(new InputEvent('input', {
data: char,
bubbles: true,
cancelable: true
}))
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}
}

function onKeydown(event: KeyboardEvent) {
// Prevent IME keydown (229) from interfering with reka-ui's segment logic.
if (isComposing.value || event.keyCode === 229) {
event.stopPropagation()
event.stopImmediatePropagation()
}
}

function onInput(event: Event) {
// Prevent browser from inserting characters directly into segments during IME composition.
if (isComposing.value || (event as InputEvent).isComposing) {
event.stopImmediatePropagation()
}
}

function setInputRef(index: number, el: Element | ComponentPublicInstance | null) {
// @ts-expect-error - ComponentPublicInstance type mismatch in Nuxt module augmentation
inputsRef.value[index] = el
Expand Down Expand Up @@ -173,6 +218,10 @@ defineExpose({
data-slot="segment"
:class="ui.segment({ class: uiProp?.segment })"
:data-segment="segment.part"
@input.capture="onInput"
@keydown.capture="onKeydown"
@compositionstart="onCompositionStart"
@compositionend="onCompositionEnd"
>
{{ segment.value.trim() }}
</DateField.Input>
Expand Down
27 changes: 27 additions & 0 deletions src/runtime/components/PinInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,30 @@ function setInputRef(index: number, el: Element | ComponentPublicInstance | null
inputsRef.value[index] = el
}

const isComposing = ref(false)

function onCompositionStart() {
isComposing.value = true
}

function onCompositionEnd(event: Event) {
isComposing.value = false
// Some browsers may not fire an input event after compositionend, or reka-ui might miss it.
// We manually dispatch an input event to ensure reka-ui receives the final value.
const target = event.target as HTMLInputElement
if (target) {
target.dispatchEvent(new Event('input', { bubbles: true }))
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

function onInput(event: Event) {
// Prevent reka-ui from shifting focus too early during IME composition,
// which causes subsequent characters to be entered into the next input field.
if (isComposing.value || (event as InputEvent).isComposing) {
event.stopImmediatePropagation()
}
}

const completed = ref(false)
function onComplete(value: string[] | number[]) {
// @ts-expect-error - 'target' does not exist in type 'EventInit'
Expand Down Expand Up @@ -144,6 +168,9 @@ defineExpose({
:disabled="disabled"
@blur="onBlur"
@focus="emitFormFocus"
@input.capture="onInput"
@compositionstart="onCompositionStart"
@compositionend="onCompositionEnd"
/>
</PinInputRoot>
</template>
Loading