Skip to content

Commit

Permalink
feat(pointer)!: change selection per pointer (#763)
Browse files Browse the repository at this point in the history
* feat!: change input selection on `mousedown`

* change selection on different elements and with offset

* stop moving selection after pointerup

BREAKING CHANGE: `userEvent.type` does no longer move the cursor
if used with `skipClick=false` and without `initialSelectionStart`.
  • Loading branch information
ph-fritsche committed Nov 28, 2021
1 parent 2a7a22e commit 17fb8b1
Show file tree
Hide file tree
Showing 15 changed files with 700 additions and 161 deletions.
38 changes: 14 additions & 24 deletions src/pointer/pointerAction.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import {Coords, wait} from '../utils'
import {wait} from '../utils'
import {pointerMove, PointerMoveAction} from './pointerMove'
import {pointerPress, PointerPressAction} from './pointerPress'
import {inputDeviceState, pointerOptions, pointerState} from './types'
import {
inputDeviceState,
pointerOptions,
pointerState,
PointerTarget,
SelectionTarget,
} from './types'

export type PointerActionTarget = {
target?: Element
coords?: Partial<Coords>
}
export type PointerActionTarget = Partial<PointerTarget> &
Partial<SelectionTarget>

export type PointerAction = PointerActionTarget &
(
Expand Down Expand Up @@ -34,12 +38,11 @@ export async function pointerAction(

const target =
action.target ?? getPrevTarget(pointerName, state.pointerState)
const coords = completeCoords({
...(pointerName in state.pointerState.position
const coords =
action.coords ??
(pointerName in state.pointerState.position
? state.pointerState.position[pointerName].coords
: undefined),
...action.coords,
})
: undefined)

const promise =
'keyDef' in action
Expand Down Expand Up @@ -70,16 +73,3 @@ function getPrevTarget(pointerName: string, state: pointerState) {

return state.position[pointerName].target as Element
}

function completeCoords({
x = 0,
y = 0,
clientX = x,
clientY = y,
offsetX = x,
offsetY = y,
pageX = clientX,
pageY = clientY,
}: Partial<Coords>) {
return {x, y, clientX, clientY, offsetX, offsetY, pageX, pageY}
}
59 changes: 50 additions & 9 deletions src/pointer/pointerMove.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import {setUISelection} from '../document'
import {
Coords,
PointerCoords,
firePointerEvent,
isDescendantOrSelf,
isDisabled,
} from '../utils'
import {inputDeviceState, PointerTarget} from './types'
import {resolveSelectionTarget} from './resolveSelectionTarget'
import {inputDeviceState, PointerTarget, SelectionTarget} from './types'

export interface PointerMoveAction extends PointerTarget {
export interface PointerMoveAction extends PointerTarget, SelectionTarget {
pointerName?: string
}

export async function pointerMove(
{pointerName = 'mouse', target, coords}: PointerMoveAction,
{pointerName = 'mouse', target, coords, node, offset}: PointerMoveAction,
{pointerState, keyboardState}: inputDeviceState,
): Promise<void> {
if (!(pointerName in pointerState.position)) {
Expand All @@ -25,6 +27,7 @@ export async function pointerMove(
pointerType,
target: prevTarget,
coords: prevCoords,
selectionRange,
} = pointerState.position[pointerName]

if (prevTarget && prevTarget !== target) {
Expand All @@ -36,7 +39,11 @@ export async function pointerMove(
}
}

pointerState.position[pointerName] = {pointerId, pointerType, target, coords}
pointerState.position[pointerName] = {
...pointerState.position[pointerName],
target,
coords,
}

if (prevTarget !== target) {
if (!prevTarget || !isDescendantOrSelf(prevTarget, target)) {
Expand All @@ -49,14 +56,44 @@ export async function pointerMove(
// Here we could probably calculate a few coords leading up to the final position
fireMove(target, coords)

function fireMove(eventTarget: Element, eventCoords: Coords) {
if (selectionRange) {
const selectionFocus = resolveSelectionTarget({target, node, offset})
if (
'node' in selectionRange &&
selectionFocus.node === selectionRange.node
) {
setUISelection(
selectionRange.node,
Math.min(selectionRange.start, selectionFocus.offset),
Math.max(selectionRange.end, selectionFocus.offset),
)
} else /* istanbul ignore else */ if ('setEnd' in selectionRange) {
const range = selectionRange.cloneRange()
const cmp = selectionRange.comparePoint(
selectionFocus.node,
selectionFocus.offset,
)
if (cmp < 0) {
range.setStart(selectionFocus.node, selectionFocus.offset)
} else if (cmp > 0) {
range.setEnd(selectionFocus.node, selectionFocus.offset)
}

// TODO: support multiple ranges
const selection = target.ownerDocument.getSelection() as Selection
selection.removeAllRanges()
selection.addRange(range.cloneRange())
}
}

function fireMove(eventTarget: Element, eventCoords?: PointerCoords) {
fire(eventTarget, 'pointermove', eventCoords)
if (pointerType === 'mouse' && !isDisabled(eventTarget)) {
fire(eventTarget, 'mousemove', eventCoords)
}
}

function fireLeave(eventTarget: Element, eventCoords: Coords) {
function fireLeave(eventTarget: Element, eventCoords?: PointerCoords) {
fire(eventTarget, 'pointerout', eventCoords)
fire(eventTarget, 'pointerleave', eventCoords)
if (pointerType === 'mouse' && !isDisabled(eventTarget)) {
Expand All @@ -65,7 +102,7 @@ export async function pointerMove(
}
}

function fireEnter(eventTarget: Element, eventCoords: Coords) {
function fireEnter(eventTarget: Element, eventCoords?: PointerCoords) {
fire(eventTarget, 'pointerover', eventCoords)
fire(eventTarget, 'pointerenter', eventCoords)
if (pointerType === 'mouse' && !isDisabled(eventTarget)) {
Expand All @@ -74,7 +111,11 @@ export async function pointerMove(
}
}

function fire(eventTarget: Element, type: string, eventCoords: Coords) {
function fire(
eventTarget: Element,
type: string,
eventCoords?: PointerCoords,
) {
return firePointerEvent(eventTarget, type, {
pointerState,
keyboardState,
Expand Down
Loading

0 comments on commit 17fb8b1

Please sign in to comment.