InputSource

Input from the Environment: the InputSource service, which gives each target's pointer, keys and focus as Input feeds, and the events they carry, in core's own terms, so no recognizer reads a platform's events. An Environment provides it, mapping its platform's events onto these, as @wardic/dom does. A target is the Environment's own, opaque to core: an element, a node in a scene graph. heldPointer is how an Environment builds a target's pointer feed: once a pointer goes down on the target, its moves and its release keep reaching it wherever they land.

import * as InputSource from "@wardic/core/InputSource"

const input = yield* InputSource.InputSource
yield* InputFeed.watch(input.pointer(card), {
  on: (event) => {
    if (event.type === "down" && InputSource.isPrimaryPointer(event)) grab(event.x, event.y)
  },
})

Classes

InputSource

Input from the Environment, as Input feeds: each target's pointer, keys and focus, shared by every subscriber to that target, and heard inside the platform's dispatch. A consumer that wants a Stream reads one through InputFeed.toStream.

Extends

  • Context.Service

Properties

adoptablePointer?
readonly adoptablePointer?: (target) => InputFeed<PointerLikeEvent>;

pointer(target) with the downs adoptPointer hands target too: for a target that can be handed one, a drag given drag controls. The plain pointer then subscribes to no handing, so an element that's only hovered or pressed pays for none. Absent: pointer delivers the handed downs itself.

Parameters
target

unknown

Returns

InputFeed<PointerLikeEvent>

adoptPointer
readonly adoptPointer: (target, raw) => Option<PointerLikeEvent>;

Hands raw, the platform's event of a pointer pressed elsewhere, to target, as drag controls start a drag from a handle: delivered on adoptablePointer(target) (pointer(target) without one) as an adopted down at its point, inside this call, and held from there as a down on the target is. The down as delivered, or none, delivering nothing, when raw isn't a pressed pointer's event.

Parameters
target

unknown

raw

unknown

Returns

Option<PointerLikeEvent>

focus
readonly focus: (target) => InputFeed<FocusLikeEvent>;

target taking and losing focus, and whatever inside it does.

Parameters
target

unknown

Returns

InputFeed<FocusLikeEvent>

focusWithin?
readonly focusWithin?: (target) => boolean;

Whether focus is now on target or inside it, from an Environment that can say. A keyboard press then holds its target's keys only while it is, as Motion's listens for keys once its element has focus, so an element at rest holds none. Absent: a keyboard press holds the keys throughout, for an Environment whose keys can reach a target without its focus.

Parameters
target

unknown

Returns

boolean

keyboard
readonly keyboard: (target) => InputFeed<KeyboardLikeEvent>;

The keys at target or inside it, heard after the app's own handlers, so a consumer reads whether one took the key and can still take it itself. A key reaches the targets it passed through innermost first, as a pointer's down does.

Parameters
target

unknown

Returns

InputFeed<KeyboardLikeEvent>

pointer
readonly pointer: (target) => InputFeed<PointerLikeEvent>;

The pointer events over target, and every held pointer's: once a pointer goes down on target, its moves, its release and its cancel keep reaching this feed wherever they land, until its release, its cancel, or a move with no button pressed. A down reaches the targets it landed in innermost first, so a press inside marks it before its ancestors' presses read it. heldPointer builds it for an Environment.

Parameters
target

unknown

Returns

InputFeed<PointerLikeEvent>

Interfaces

FocusLikeEvent

A target taking or losing focus, in core's terms.

Properties

fromTarget?
readonly fromTarget?: boolean;

Whether it's the target's own focus or blur. false: something inside it took or lost focus, which only cancels a keyboard press that reached the target from inside; Recognizer.focus counts the target's own only. Absent: the target's own.

raw?
readonly raw?: unknown;

The platform's own event, which core never reads: a blur that cancels a keyboard press hands it to an app's callback.

timeStamp
readonly timeStamp: number;

When it happened, in ms.

type
readonly type: "focus" | "blur";

Whether it took focus or lost it.

visible?
readonly visible?: boolean;

Whether the Environment shows this focus: a keyboard's, say, and not a click's. Absent: shown. Recognizer.focus's visible counts only these.


HeldPointerOptions

The feeds heldPointer merges into a target's pointer feed.

Properties

adopted?
readonly adopted?: PointerFeed;

The downs handed to the target from elsewhere, by InputSource's adoptPointer: each held as a down on the target is, and delivered adopted. Its other events are left out.

global?
readonly global?: PointerFeed;

Pointer events anywhere in the Environment, subscribed only while a pointer is held.

scroll?
readonly scroll?: ScrollFeed;

Scroll positions, subscribed only while a pointer is held: a scroll moves a held pointer.

target
readonly target: PointerFeed;

The pointer events on the target itself.


KeyboardLikeEvent

A key going down or up at a target or inside it, in core's terms.

Properties

action
readonly action: KeyAction;

What the key's own target does with it: a keyboard press on Space takes the key only where its target does nothing.

fromTarget
readonly fromTarget: boolean;

Whether it came from the target itself, not from something inside it, a text field or a button.

handle
readonly handle: () => void;

Takes the key, so the platform's own response to it, a scroll say, doesn't run: called while handling the event, which arrives inside the platform's dispatch.

Returns

void

handled
readonly handled: boolean;

Whether a handler that ran before this one, the app's, already took the key.

key
readonly key: Key;

Which key.

raw?
readonly raw?: unknown;

The platform's own event, which core never reads, as a pointer event's raw: a binding hands it to an app's callback. One key that reaches several targets carries the same raw on each copy.

repeat
readonly repeat: boolean;

Whether it's a held key's repeat.

timeStamp
readonly timeStamp: number;

When it happened, in ms.

type
readonly type: "down" | "up";

Whether the key went down or up.


PointerLikeEvent

A pointer's event, in core's terms: a mouse's, a pen's or a finger's. An Environment maps its platform's events onto these, so a native or a canvas Environment gives the same.

Properties

adopted?
readonly adopted?: boolean;

true on a down the target adopted: its pointer went down elsewhere, on a handle or a track, and InputSource's adoptPointer handed it to the target, whose feed holds it from there as if it had gone down on the target. Only a drag starts from it; a press, a hover or a pan leaves it out. Absent on every other event, the adopted pointer's later ones included.

button
readonly button: PointerButton;

The button whose state it changed: "none" when none did, as on a move.

kind
readonly kind: PointerKind;

What the pointer is: a mouse, a pen, a finger.

onTarget
readonly onTarget: boolean;

Whether it landed on the target or inside it: the target's own events do, and a held pointer's events from elsewhere say where they landed. A release that does is a tap; one that doesn't cancels it.

pointerId
readonly pointerId: number;

Which pointer: the same on all its events while it's down.

pressed
readonly pressed: boolean;

Whether any button is down. A pressed pointer's moves are pressed, so a held pointer's move with none pressed means its release was lost: a drag ends there.

primary
readonly primary: boolean;

Whether it's the first of its kind in contact: the mouse always is, a second finger isn't. It isn't the primary pointer itself: isPrimaryPointer decides that, from it.

raw?
readonly raw?: unknown;

The platform's own event this one was mapped from, which core never reads: a binding hands it to an app's gesture callback. A move a scroll made, and a lost release's ending move, carry the pointer's last move's, or its down's before it has moved; never an enter's or a leave's.

One platform event that reaches several targets carries the same raw on each copy: Press claims find the event by it. An Environment with no platform event hands every target the same event object instead.

scroll
readonly scroll: Point;

The scroll its hold has counted into x and y since its down: zero for a pointer that isn't held, or held with nothing scrolled. x - scroll.x is where the pointer is on the screen. heldPointer sets it.

timeStamp
readonly timeStamp: number;

When it happened, in ms.

type
readonly type: "move" | "down" | "up" | "cancel" | "enter" | "leave";

What happened: a press ("down"), a move, a release ("up"), a cancel by the platform, or the pointer coming over the target ("enter") or going off it ("leave").

x
readonly x: number;

Where it is across, in the Environment's coordinates, with a held pointer's scroll counted in.

y
readonly y: number;

Where it is down, in the Environment's coordinates, with a held pointer's scroll counted in.

Type Aliases

Key

type Key = 
  | "space"
  | "enter"
  | "escape"
  | "left"
  | "right"
  | "up"
  | "down"
  | "home"
  | "end"
  | "other";

A key, in core's names: the keys core acts on, and "other" for the rest. An Environment maps its platform's key names onto these, so no platform's names reach core.


KeyAction

type KeyAction = "type" | "activate" | "none";

What a key's target does with it itself, unless something takes it: "type", the key goes into text the target edits; "activate", the target has its own action for it; "none", nothing of its own, though its Environment may. An Environment says it of its own targets, so core knows none of their kinds.


PointerButton

type PointerButton = "main" | "middle" | "secondary" | "other" | "none";

A pointer's button, in core's words: "none" when an event changed no button, "other" for one core has no rule about.


PointerFeed

type PointerFeed = (emit) => () => void;

A source of an Environment's pointer events, for heldPointer: called with emit, it calls it with each event and returns the function that stops it. raw is the platform's event the PointerLikeEvent was mapped from, the same object for the same event on every feed: it's how an event that reaches two feeds is delivered once.

Parameters

emit

(event, raw) => void

Returns

() => void


PointerKind

type PointerKind = "mouse" | "pen" | "touch" | "other";

What a pointer is, in core's words, "other" for a kind core has no rule about. An Environment maps its platform's kinds onto these.


ScrollFeed

type ScrollFeed = (emit) => () => void;

A source of scroll positions, for heldPointer: called with emit, it reports where every scroller it tracks is at once (the Environment's own, the target's scrolling ancestors), then each new position as it scrolls, and returns the function that stops it. source is the same object for the same scroller.

Parameters

emit

(source, position, timeStamp) => void

Returns

() => void

Variables

NO_SCROLL

const NO_SCROLL: Point;

No scroll counted, { x: 0, y: 0 }: shared, so an event that isn't held allocates nothing for it.

Functions

adoptablePointerOf()

function adoptablePointerOf(input, target): InputFeed<PointerLikeEvent>;

target's pointer feed with the downs adoptPointer hands it: input's adoptablePointer, or its pointer when it has none, which then delivers them itself.

Parameters

input
adoptablePointer?

(target) => InputFeed<PointerLikeEvent>

pointer(target) with the downs adoptPointer hands target too: for a target that can be handed one, a drag given drag controls. The plain pointer then subscribes to no handing, so an element that's only hovered or pressed pays for none. Absent: pointer delivers the handed downs itself.

adoptPointer

(target, raw) => Option<PointerLikeEvent>

Hands raw, the platform's event of a pointer pressed elsewhere, to target, as drag controls start a drag from a handle: delivered on adoptablePointer(target) (pointer(target) without one) as an adopted down at its point, inside this call, and held from there as a down on the target is. The down as delivered, or none, delivering nothing, when raw isn't a pressed pointer's event.

focus

(target) => InputFeed<FocusLikeEvent>

target taking and losing focus, and whatever inside it does.

focusWithin?

(target) => boolean

Whether focus is now on target or inside it, from an Environment that can say. A keyboard press then holds its target's keys only while it is, as Motion's listens for keys once its element has focus, so an element at rest holds none. Absent: a keyboard press holds the keys throughout, for an Environment whose keys can reach a target without its focus.

keyboard

(target) => InputFeed<KeyboardLikeEvent>

The keys at target or inside it, heard after the app's own handlers, so a consumer reads whether one took the key and can still take it itself. A key reaches the targets it passed through innermost first, as a pointer's down does.

pointer

(target) => InputFeed<PointerLikeEvent>

The pointer events over target, and every held pointer's: once a pointer goes down on target, its moves, its release and its cancel keep reaching this feed wherever they land, until its release, its cancel, or a move with no button pressed. A down reaches the targets it landed in innermost first, so a press inside marks it before its ancestors' presses read it. heldPointer builds it for an Environment.

target

unknown

Returns

InputFeed<PointerLikeEvent>


heldPointer()

function heldPointer(options): InputFeed<PointerLikeEvent>;

A target's pointer feed, for an Environment's InputSource: once a pointer goes down on the target, its moves, its release and its cancel keep reaching the feed wherever they land, until it's released. The Environment hands it its own feeds (HeldPointerOptions), and each event reaches the consumer inside the platform's dispatch.

  • target: the pointer's events on the target, for the feed's whole life.
  • global: pointer events anywhere in the Environment, subscribed only while a pointer that went down on the target is held, from inside that down, so no event after it is missed. A platform that keeps a held pointer on its target already, as native touch does, leaves it out.
  • adopted: the downs handed to the target from elsewhere, held as a down on it is.
  • scroll: a scroll moves a held pointer, as Motion's pan counts it, since what's under it moved. Each scroll moves every held pointer that has moved since its down, and its later events arrive shifted by the scroll since then. Before its first move a scroll only adds to the shift, so a press held still while what's under it scrolls stays a press.

A hold ends at its pointer's release, its cancel, or a move with no button pressed: a lost release. A new down by the held pointer, or by a new pointer of its kind, means its release was lost too: the hold ends with a move at its last point, with no button pressed, before the down reaches any target.

Parameters

options

HeldPointerOptions

Returns

InputFeed<PointerLikeEvent>

Example

const pointer = InputSource.heldPointer({ target: onTarget, global: onPage, scroll: onScroll })

isPrimaryPointer()

function isPrimaryPointer(event): boolean;

Whether event is the primary pointer's, the only one drags and presses hear: a mouse's with its main button or no button change, or any other pointer that's primary. A right-click doesn't drag, and a second finger neither presses nor moves a drag. As Motion's isPrimaryPointer; for a recognizer of your own.

Parameters

event

PointerLikeEvent

Returns

boolean


makePointerLikeEvent()

function makePointerLikeEvent(init): PointerLikeEvent;

A pointer event with a mouse's defaults, for a test's script or an Environment with only a mouse: pointer 1 at (0, 0) at time 0, a primary mouse, its button "main" on a down or an up and "none" otherwise, pressed on a down or a move, no scroll, on the target. A field given wins.

Parameters

init

object & Partial<Omit<PointerLikeEvent, "type">>

Returns

PointerLikeEvent

Example

const move = InputSource.makePointerLikeEvent({ type: "move", x: 40, y: 10 })