Util

isComposing

Predicate that returns true while an input method is composing — the guard that keeps Enter, Escape and the arrows with the IME's candidate window instead of your component.

Usage

A Japanese or Chinese user reaches every character through a conversion. Between compositionstart and compositionend a candidate window is open, and Enter accepts a candidate, Escape cancels the reading back to kana, and the arrows walk the candidate list. A handler that acts on those keys anyway takes them from the input method mid-word.

import { isComposing } from 'ngwr/utils';

// The first line of every keydown handler over a field that accepts typed text.
onKeydown(event: KeyboardEvent): void {
  if (isComposing(event)) return;

  switch (event.key) {
    case 'Enter': this.commit(); break;
    case 'Escape': this.close(); break;
  }
}

Why ngwr provides this

event.isComposing alone is the guard everyone writes, and it has a hole. Safari fires compositionend before the keydown of the keystroke that commits a candidate, so the one press that most needs guarding arrives with the flag already false — carrying only the legacy keyCode of 229, the sentinel every engine sets on a key it handed to the input method.

// Native — the standard flag, and it misses Safari.
if (event.isComposing) return;
// → Safari fires `compositionend` BEFORE the keydown of the Enter that accepts
//   a candidate, so that one arrives with isComposing === false and commits the
//   half-composed word anyway.

// ngwr — the flag plus the 229 sentinel every engine sets on an IME's key.
if (isComposing(event)) return;

Knowing the state between events

This predicate is scoped to keydown. It does not answer “is a composition in progress” for an input or blur handler — on the committing keystroke the key flag and the composition state disagree by design. A component that needs the state between events tracks the compositionstart / compositionend pair, which is what wr-mention and wr-input-otp do.

// `isComposing` answers "did the input method take this KEY". For "is a
// conversion open right now" — which is what an `input` or `blur` handler needs
// — track the pair instead: on the committing keystroke the two disagree.
@Component({
  host: {
    '(compositionstart)': 'composing = true',
    '(compositionend)': 'composing = false; onSettledInput()',
    '(input)': 'composing ? null : onSettledInput()',
  },
})

API

NameDescriptionTypeDefault
isComposing(event)True while an input method is composing, so the key belongs to the IME rather than to your handler. Return early on it in every keydown handler that reads Enter, Escape or an arrow over a field that accepts typed text.(e: KeyboardEvent) => boolean

See also