e03c002efd
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.
91 lines
1.8 KiB
JavaScript
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()
|
|
}
|
|
}
|