Files
fizzy/app/javascript/controllers/form_controller.js
T
gijigae e03c002efd Fix Japanese IME input handling for tags and form fields
When typing in Japanese, the Enter key is used to confirm kanji conversion
during IME composition. Previously, pressing Enter would immediately save
the tag, which prevented users from completing the conversion.

Changes:
- Add isComposing check to navigable_list_controller.js Enter handler
- Add IME composition tracking to form_controller.js
- Add compositionstart/compositionend event handlers to tag, step, and
  reaction input fields
- Add preventComposingSubmit action to prevent form submission during
  composition

This fix supports all IME input methods (Japanese, Chinese, Korean, etc.)
using standard browser composition events.
2025-12-06 17:26:35 +09:00

91 lines
1.8 KiB
JavaScript

import { Controller } from "@hotwired/stimulus"
import { debounce, nextFrame } from "helpers/timing_helpers";
export default class extends Controller {
static targets = [ "cancel", "submit", "input" ]
static values = {
debounceTimeout: { type: Number, default: 300 }
}
#isComposing = false
initialize() {
this.debouncedSubmit = debounce(this.debouncedSubmit.bind(this), this.debounceTimeoutValue)
}
// IME Composition tracking
compositionStart() {
this.#isComposing = true
}
compositionEnd() {
this.#isComposing = false
}
submit() {
this.element.requestSubmit()
}
preventEmptySubmit(event) {
const input = this.hasInputTarget ? this.inputTarget : null
if (input) {
const value = (input.value || "").trim()
const isEmpty = value.length === 0
if (isEmpty) {
event.preventDefault()
input.setCustomValidity(input.dataset.validationMessage || "Please fill out this field")
input.reportValidity()
input.addEventListener("input", () => input.setCustomValidity(""), { once: true })
}
}
}
preventComposingSubmit(event) {
if (this.#isComposing) {
event.preventDefault()
}
}
debouncedSubmit(event) {
this.submit(event)
}
submitToTopTarget(event) {
this.element.setAttribute("data-turbo-frame", "_top")
this.submit()
}
reset() {
this.element.reset()
}
cancel() {
this.cancelTarget?.click()
}
preventAttachment(event) {
event.preventDefault()
}
async disableSubmitWhenInvalid(event) {
await nextFrame()
if (this.element.checkValidity()) {
this.submitTarget.removeAttribute("disabled")
} else {
this.submitTarget.toggleAttribute("disabled", true)
}
}
select(event) {
event.target.select()
}
blurActiveInput() {
document.activeElement?.blur()
}
}