Skip to content

[Umbrella] React Flare #15257

Closed
Closed
@necolas

Description

@necolas

This issue tracks tasks related to the React DOM implementation of the experimental React Events API (react-events) aka React Flare (our internal code-name for the project). The idea is to extend React's event system to include high-level events that allow for consistent cross-device and cross-platform behavior.

Note: For now this is completely experimental and won't affect the open source builds of React.

Core

  • Document specific UX patterns that this enables and fixes. (inc. from my workplace post on styles, Dan & Brian dev tools, apps, etc.)
  • Every user-facing event should have a timeStamp. Should we provide a standard way to derive data that might be common across events and require different logic for different positional information, e.g., point, delta, offset, etc?
  • How to test discrete vs continuous events?
  • Should we polyfill pointer events or not dispatch emulated touch/mouse events in the core, to avoid multiple event components needing to implement the same logic to ignore emulated events?
  • Investigate native pointer capture for retargeting events - https://www.w3.org/TR/pointerevents/#pointer-capture. This could be particularly useful for drag/swipe UX.
  • Limit all event components to have a single host node as child? If not enforced for all components, we might need this for Hover/Focus/Press at least.
  • FYI Always use event capturing. Remove media events. #12919
  • Optimize bundle output.
  • Replace magic object export with React.unstable_createEventComponent(type: Symbol | number, responder: ReactEventResponder, displayName: ?string)
  • Document core API for building responders
  • Stop propagation by default but scope to event module types.
  • Investigate ways to listen to root events without first needing to wait for a target event to be dispatched to the event component.
  • Remove listener from event object passed to callbacks like onPress
  • Ensure discrete events do not unnecessarily force flush previous non-discrete events.
  • Ensure event responder get unmounted correctly and pending events (e.g. long press) properly get removed when doing so. Maybe an unmount lifecycle method needs adding to event responders?
  • Create a new synthetic event and dispatch mechanism that relies on the event being generated in the event responder module, rather than from React. This allows for the event to be better typed and have a much simpler implementation for how React fires the event when dispatched + it means we can avoid pulling in the current SyntheticEvent system and all its dependencies.

Ownership

  • Decide on prop name equivalents to RN onStartShouldSetResponder and onMoveShouldSetResponder.

Focus module

  • Determine the potential use cases for DOM focusin and focusout events.
  • Cross browser and device/modality testing.
  • add focusVisible functionality
  • Add README
  • Write unit tests
    • disabled
    • onBlur
    • onFocus
    • onFocusChange
    • onFocusVisibleChange
  • Determine event object data (might require a global "modality" tracker to help attach this info to focus/blur events)
{
  pointerType: 'mouse' | 'pen' | 'touch' | 'keyboard'
}

Hover module

  • Cross browser and device/modality testing.
  • Determine event object data.
{
  pointerType: 'mouse' | 'pen',
  point: { x: number, y: number }
}

Press module

  • Cross browser and device/modality testing.
  • Behaviour for selecting text within pressable. End press on selection event? Add a new props to configure the behaviour, like onMoveShouldEndPress?
  • Allow contextMenu to display when element is a link and ctrl is held down during press
  • Cancel long press if active press moves (exceeding threshold, IIRC RN uses 10px delta)
  • Reactivate when moving out-of-bounds press back into the responder region.
  • BUG: press start -> move out of target -> release -> tap press. The second press doesn't cause onPressStart/Change to be called, even thought those events are dispatched to the core responder system. (Event API: Fix bug where Press root events were not being cleared #15507)
  • FYI: touchAction:'none' is needed on currentTarget to prevent browser cancelling after pointermove.
  • Prevent contextMenu appearing during a long press on touch screens.
  • Account for UX involving interactions on links with modifier keys held.
  • Add pressRetentionOffset to control when press is released after moving the pointer away from the press target.
  • Add README
  • Always prevent default on clicks. Maybe have an override to turn off behaviour?
  • Rename events to onPressStart, onPressEnd (Rename press props in experimental event API #15263).
  • Change longPressCancelsPress to onLongPressShouldCancelPress (Rename press props in experimental event API #15263).
  • Change default delayLongPress to 500ms (see note in Add tests for Press responder event module #15290)
  • Add keyboard support for all events.
  • Prevent scroll-down page when pressing Spacebar on keyboard to interact
  • Add delayPressStart and delayPressEnd props.
  • Write unit tests (Add tests for Press responder event module #15290)
    • disabled
    • onLongPress
    • onLongPressChange
    • onLongPressShouldCancelPress
    • onPress
    • onPressChange
    • onPressStart
    • onPressEnd
    • delayLongPress
    • delayPressStart
    • delayPressEnd
    • pressRententionOffset / pressRect
    • emulated mouse events
    • fix keyboard press events when metaKey is involved (this currently works the same way as native, so will leave it for now)
    • any special case anchor tag-related tests
    • hitslop interactions
  • Determine event object data.
{
  pointerType: 'mouse' | 'pen' | 'touch' | 'keyboard',
  initial: { x: number, y: number },
  point: { x: number, y: number },
  delta:  { x: number, y: number }
}

FocusScope module

Consider adding onFocusIn and onFocusOut (names tbd) to support userland customisation of focus repair. We could return the native elements.

A use case to consider: being able to programmatically move focus to an element without
allowing keyboards to focus the element (e.g., the document body, the root of a modal). In this
case the element (other than a few special cases) must have tabIndex={-1}.

InputScope module

  • onChange fires when an input element has been changed. This applies to <input>, <textarea>, <select> and elements with contenteditable or when designMode is enabled. onChange provides a callback with the event for the element that changed and a key of the elment that changed (if a key was supplied).
  • onValueChange is similar to onChange, but only provides the value changed. This applies to <input>, <textarea>, <select> and elements with contenteditable or when designMode is enabled. onValueChange provides a value and key of the element that changed (if a key was supplied).
  • onSubmit for when any <form> elements trigger form submit.
  • onKeyPress for when any keyboard keys are pressed.
  • preventKeys accepts an array of key strings that will get prevented from entering input.
  • onSelectionChange for when any any text selection occurs in any child elements.
  • onBeforeChange fires before a change is about to occur. This applies to <input>, <textarea>, <select> and elements with contenteditable or when designMode is enabled. onBeforeChange provides a callback with the event for the element that changed and a key of the elment that changed (if a key was supplied).

Drag module

  • Determine event object data (same as Pan)
  • Cancelling drag.
  • FYI: touchAction:'none' is needed on currentTarget to prevent browser cancelling after pointermove.
  • FYI: Firefox might have problems when combining mousemove with display:flex.
  • Add README
  • Cross browser and device/modality testing.
  • Write unit tests.

Pan module

  • Write unit tests.
  • Cross browser and device/modality testing.
  • Cancelling pan.
  • Add README
  • FYI: touchAction:'none' is needed on currentTarget to prevent browser cancelling after pointermove.
  • Determine event object data.
{
  pointerType: 'mouse' | 'pen' | 'touch',
  initial: { x: number, y: number },
  point: { x: number, y: number },
  velocity: { x: number, y: number },
  delta:  { x: number, y: number }
}

Scroll module

Swipe module

  • Combine onSwipe{Left,Right,Up,Down} into onSwipe w/ event data.
  • Cancelling swipe.
  • Write comprehensive unit tests.
  • Cross browser and device/modality testing.
  • Add README
  • Determine event object data (same as Pan)
  • FYI: touchAction:'none' is needed on currentTarget to prevent browser cancelling after pointermove.

Touch HitSlop

Consider whether we need this at all. Some browsers have a native hitslop and we could work with vendors on any potential improvements to the native system

Dev Tools (#15267)

  • Add displayName fields to event components and event targets.
  • Possibly add a description or displayName field to event responders? For example the Press responder module for ReactDOM could have the name ReactDOMPressResponder.
  • Expose some DEV only event triggering exports from event responder modules. i.e. HoverEventResponder.DevToolEvents = [{ name: 'hover', trigger: [{ name: 'pointerup', passive: false }]];
  • Add basic support for rendering event responders and event targets in the tree. @bvaughn

Ancillary work

  • Investigate removing object assign polyfill from individual event modules
  • Add internal interactive documentation / fiddle
  • Investigate press event patterns on input, textarea, select, etc.
  • Implement high-level components like Pressable (inc delays).
  • Nested Pressables should not bubble events.
  • Investigate accounting for element resizes when determining hit bounds, e.g., using resize observer for notifications

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions