InputCombination
InputCombination builds conditions from keyboard, mouse, and gamepad state. Conditions can represent a single control, a chord, alternatives, exclusions, or an ordered sequence.
import {
Input,
InputCombination
} from "@jolly-pixel/controls";
const canvas = document.querySelector("canvas");
if (!canvas) {
throw new Error("No canvas element found");
}
const input = new Input(canvas);
input.connect();
const save = InputCombination.all(
InputCombination.Mod,
"KeyS.pressed"
);
const moveUp = InputCombination.MoveUp;
const selectWithoutShift = InputCombination.all(
InputCombination.mouse("left", "pressed"),
InputCombination.none("ShiftLeft.down")
);
function gameLoop() {
input.update();
if (save.evaluate(input)) {
console.log("Save");
}
if (moveUp.evaluate(input)) {
console.log("Move up");
}
if (selectWithoutShift.evaluate(input)) {
console.log("Select");
}
requestAnimationFrame(gameLoop);
}
gameLoop();Every factory returns an InputCondition. A condition can be evaluated directly or bound to an Input as a function. Stateful conditions, including sequences, also expose reset().
Action types
type CombinedInputState =
| "down"
| "pressed"
| "released";
type CombinedKeyboardInputAction =
`${ExtendedKeyCode}.${CombinedInputState}`;
type CombinedMouseInputAction =
`${MouseAction}.${CombinedInputState}`;
type CombinedInputAction =
| CombinedKeyboardInputAction
| CombinedMouseInputAction;"down" remains true while the control is held. "pressed" and "released" read the corresponding transition from the latest device update.
These types are exported from the package root. Keyboard and mouse actions use separate types so a mouse action cannot reach a keyboard condition by mistake.
Keyboard conditions
InputCombination.key(
action: CombinedKeyboardInputAction
): AtomicInput
InputCombination.key(
key: InputKeyboardAction,
state?: CombinedInputState
): AtomicInputCreates a keyboard condition. The state defaults to "pressed".
InputCombination.key("Space");
InputCombination.key("KeyW", "down");
InputCombination.key("ShiftLeft.released");
InputCombination.key("ANY", "pressed");
InputCombination.key("NONE", "down");InputKeyboardAction includes the "ANY" and "NONE" sentinels. They query the selected state across the whole keyboard. Sentinels use the separate state argument; combined forms such as "ANY.down" are not part of CombinedKeyboardInputAction.
Mouse conditions
InputCombination.mouse(
action: CombinedMouseInputAction
): AtomicInput
InputCombination.mouse(
button: InputMouseAction,
state?: CombinedInputState
): AtomicInputCreates a mouse button or virtual wheel-button condition. The state defaults to "pressed". InputMouseAction also accepts a button index and the "ANY" and "NONE" sentinels, which use the separate state argument.
InputCombination.mouse("left");
InputCombination.mouse("right", "down");
InputCombination.mouse("scrollUp.pressed");
InputCombination.mouse("ANY", "down");Gamepad conditions
InputCombination.gamepad(
gamepad: GamepadIndex,
button: number | keyof typeof GamepadButton,
state?: CombinedInputState
): AtomicInputCreates a gamepad button condition. The state defaults to "pressed".
InputCombination.gamepad(0, "A");
InputCombination.gamepad(0, "LeftBumper", "down");The AtomicInput class returned by these methods is exported from the package root.
Combined-action detection
InputCombination.isCombinedAction(
action: unknown
): action is CombinedInputActionReturns true when action is a string whose segment after the first period is a CombinedInputState. It does not validate the key or mouse action segment.
Composite conditions
Composite methods accept existing condition objects and combined keyboard actions:
type ConditionArgument =
| InputCondition
| CombinedKeyboardInputAction;ConditionArgument is used here to shorten the signatures. It is not exported. A string passed directly to a composite is a keyboard action. Create mouse and gamepad conditions with their factories before passing them to a composite.
InputCombination.all()
InputCombination.all(
...conditions: ConditionArgument[]
): AllInputsReturns true when every child returns true during the same evaluation. This is used for chords and modifier guards. An empty all() condition returns true.
InputCombination.all(
"ControlLeft.down",
"ShiftLeft.down",
"KeyS.pressed"
);InputCombination.atLeastOne()
InputCombination.atLeastOne(
...conditions: ConditionArgument[]
): AtLeastOneInputReturns true when at least one child returns true. An empty condition returns false.
InputCombination.atLeastOne(
"KeyW.down",
"ArrowUp.down"
);InputCombination.none()
InputCombination.none(
...conditions: ConditionArgument[]
): NoneInputsReturns true when every child returns false. An empty condition returns true.
InputCombination.none(
"ShiftLeft.down",
"ShiftRight.down"
);The concrete composite classes and their direct constructors are documented with the condition API.
Predefined combinations
InputCombination provides aliases for paired modifier keys, Enter, and common movement keys. A preset reads the "down" state; its pressed and released properties expose group transitions.
See AliasedKeyInput and predefined combinations for the available presets and the transition rules for an alias family.
Sequences
sequence() and sequenceWithTimeout() match ordered input. A sequence may also contain held steps that must remain active until later steps complete.
See Sequences and held steps for timeout, rollback, completion, and reset behavior.
Binding conditions
bindInputCondition(condition, input) returns a callable condition for the supplied Input.
See Binding to an Input for the bound function contract and state-sharing behavior.