From 7928a0d9da491c85c230f795df2467232c54741e Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Tue, 17 Sep 2019 12:04:31 -0700 Subject: [PATCH 1/5] typescript(vx-event): Re-write package in TypeScript --- packages/vx-event/package.json | 1 + packages/vx-event/src/getXAndYFromEvent.ts | 17 +++++++ packages/vx-event/src/{index.js => index.ts} | 0 packages/vx-event/src/localPoint.js | 50 ------------------- packages/vx-event/src/localPoint.ts | 15 ++++++ packages/vx-event/src/localPointGeneric.ts | 33 ++++++++++++ packages/vx-event/src/touchPoint.js | 21 -------- packages/vx-event/src/touchPoint.ts | 1 + packages/vx-event/src/typeGuards.ts | 27 ++++++++++ ...{localPoint.test.js => localPoint.test.ts} | 0 10 files changed, 94 insertions(+), 71 deletions(-) create mode 100644 packages/vx-event/src/getXAndYFromEvent.ts rename packages/vx-event/src/{index.js => index.ts} (100%) delete mode 100644 packages/vx-event/src/localPoint.js create mode 100644 packages/vx-event/src/localPoint.ts create mode 100644 packages/vx-event/src/localPointGeneric.ts delete mode 100644 packages/vx-event/src/touchPoint.js create mode 100644 packages/vx-event/src/touchPoint.ts create mode 100644 packages/vx-event/src/typeGuards.ts rename packages/vx-event/test/{localPoint.test.js => localPoint.test.ts} (100%) diff --git a/packages/vx-event/package.json b/packages/vx-event/package.json index 04d22e174..0e8cf0c85 100644 --- a/packages/vx-event/package.json +++ b/packages/vx-event/package.json @@ -5,6 +5,7 @@ "sideEffects": false, "main": "lib/index.js", "module": "esm/index.js", + "types": "lib/index.d.ts", "files": [ "lib", "esm" diff --git a/packages/vx-event/src/getXAndYFromEvent.ts b/packages/vx-event/src/getXAndYFromEvent.ts new file mode 100644 index 000000000..ffaba0553 --- /dev/null +++ b/packages/vx-event/src/getXAndYFromEvent.ts @@ -0,0 +1,17 @@ +import { isTouchEvent } from './typeGuards'; + +export default function getXAndYFromEvent(event?: MouseEvent | TouchEvent) { + if (!event) return { x: 0, y: 0 }; + + if (isTouchEvent(event)) { + return { + x: event.changedTouches[0].clientX, + y: event.changedTouches[0].clientX, + }; + } else { + return { + x: event.clientX, + y: event.clientY, + }; + } +} diff --git a/packages/vx-event/src/index.js b/packages/vx-event/src/index.ts similarity index 100% rename from packages/vx-event/src/index.js rename to packages/vx-event/src/index.ts diff --git a/packages/vx-event/src/localPoint.js b/packages/vx-event/src/localPoint.js deleted file mode 100644 index b4d36109e..000000000 --- a/packages/vx-event/src/localPoint.js +++ /dev/null @@ -1,50 +0,0 @@ -import { Point } from '@vx/point'; - -export default function localPoint(nodeOrEvent, maybeEvent) { - // called with no args - if (!nodeOrEvent) return; - - let node = nodeOrEvent; - let event = maybeEvent; - - // called with localPoint(event) - if (nodeOrEvent.target) { - event = nodeOrEvent; - - // set node to targets owner svg - node = event.target.ownerSVGElement; - - // find the outermost svg - while (node.ownerSVGElement) { - node = node.ownerSVGElement; - } - } - - // default to mouse event - let { clientX, clientY } = event; - - // support touch event - if (event.changedTouches) { - clientX = event.changedTouches[0].clientX; - clientY = event.changedTouches[0].clientY; - } - - // calculate coordinates from svg - if (node.createSVGPoint) { - let point = node.createSVGPoint(); - point.x = clientX; - point.y = clientY; - point = point.matrixTransform(node.getScreenCTM().inverse()); - return new Point({ - x: point.x, - y: point.y, - }); - } - - // fallback to calculating position from non-svg dom node - const rect = node.getBoundingClientRect(); - return new Point({ - x: clientX - rect.left - node.clientLeft, - y: clientY - rect.top - node.clientTop, - }); -} diff --git a/packages/vx-event/src/localPoint.ts b/packages/vx-event/src/localPoint.ts new file mode 100644 index 000000000..62ee8bc3a --- /dev/null +++ b/packages/vx-event/src/localPoint.ts @@ -0,0 +1,15 @@ +import localPointGeneric from './localPointGeneric'; +import { isElement, isEvent } from './typeGuards'; + +export default function localPoint( + nodeOrEvent: Element | MouseEvent | TouchEvent, + maybeEvent?: MouseEvent | TouchEvent, +) { + if (isElement(nodeOrEvent) && maybeEvent) { + return localPointGeneric(nodeOrEvent, maybeEvent); + } else if (isEvent(nodeOrEvent)) { + const node = nodeOrEvent.target as Element; + if (node) return localPointGeneric(node, nodeOrEvent); + } + return; +} diff --git a/packages/vx-event/src/localPointGeneric.ts b/packages/vx-event/src/localPointGeneric.ts new file mode 100644 index 000000000..935bb9fb0 --- /dev/null +++ b/packages/vx-event/src/localPointGeneric.ts @@ -0,0 +1,33 @@ +import { Point } from '@vx/point'; +import { isSVGElement, isSVGGraphicsElement, isSVGSVGElement } from './typeGuards'; +import getXAndYFromEvent from './getXAndYFromEvent'; + +export default function localPoint(node: Element, event: MouseEvent | TouchEvent) { + if (!node || !event) return; + + const coords = getXAndYFromEvent(event); + + // find top-most SVG + const svg = isSVGElement(node) ? node.ownerSVGElement : node; + const screenCTM = isSVGGraphicsElement(node) ? node.getScreenCTM() : null; + + if (isSVGSVGElement(svg) && screenCTM) { + let point = svg.createSVGPoint(); + point.x = coords.x; + point.y = coords.y; + point = point.matrixTransform(screenCTM.inverse()); + + return new Point({ + x: point.x, + y: point.y, + }); + } + + // fall back to bounding box + const rect = node.getBoundingClientRect(); + + return new Point({ + x: coords.x - rect.left - node.clientLeft, + y: coords.y - rect.top - node.clientTop, + }); +} diff --git a/packages/vx-event/src/touchPoint.js b/packages/vx-event/src/touchPoint.js deleted file mode 100644 index 4aa26ffe3..000000000 --- a/packages/vx-event/src/touchPoint.js +++ /dev/null @@ -1,21 +0,0 @@ -import { Point } from '@vx/point'; - -export default function touchPoint(node, event) { - if (!node) return; - const svg = node.ownerSVGElement || node; - if (svg.createSVGPoint) { - let point = svg.createSVGPoint(); - point.x = event.changedTouches[0].clientX; - point.y = event.changedTouches[0].clientY; - point = point.matrixTransform(node.getScreenCTM().inverse()); - return new Point({ - x: point.x, - y: point.y, - }); - } - const rect = node.getBoundingClientRect(); - return new Point({ - x: event.changedTouches[0].clientX - rect.left - node.clientLeft, - y: event.changedTouches[0].clientY - rect.top - node.clientTop, - }); -} diff --git a/packages/vx-event/src/touchPoint.ts b/packages/vx-event/src/touchPoint.ts new file mode 100644 index 000000000..b392c2c4a --- /dev/null +++ b/packages/vx-event/src/touchPoint.ts @@ -0,0 +1 @@ +export { default } from './localPointGeneric'; diff --git a/packages/vx-event/src/typeGuards.ts b/packages/vx-event/src/typeGuards.ts new file mode 100644 index 000000000..4c1fd4a55 --- /dev/null +++ b/packages/vx-event/src/typeGuards.ts @@ -0,0 +1,27 @@ +export function isElement(elem?: Element | Event): elem is Element { + return !!elem && elem instanceof Element; +} + +// functional definition of isSVGElement. Note that SVGSVGElements are HTMLElements +export function isSVGElement(elem?: Element): elem is SVGElement { + return !!elem && (elem instanceof SVGElement || 'ownerSVGElement' in elem); +} + +// functional definition of SVGGElement +export function isSVGSVGElement(elem?: Element | null): elem is SVGSVGElement { + return !!elem && 'createSVGPoint' in elem; +} + +export function isSVGGraphicsElement(elem?: Element | null): elem is SVGGraphicsElement { + return !!elem && 'getScreenCTM' in elem; +} + +// functional definition of TouchEvent +export function isTouchEvent(event?: Event): event is TouchEvent { + return !!event && 'changedTouches' in event; +} + +// functional definition of event +export function isEvent(event?: Event | Element): event is Event { + return !!event && 'target' in event; +} diff --git a/packages/vx-event/test/localPoint.test.js b/packages/vx-event/test/localPoint.test.ts similarity index 100% rename from packages/vx-event/test/localPoint.test.js rename to packages/vx-event/test/localPoint.test.ts From a00d9656e118ebf6b61dbb6c1974c75cedfed003 Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Tue, 17 Sep 2019 14:18:20 -0700 Subject: [PATCH 2/5] types(vx-event): add @types/react and support React.SyntheticEvents --- packages/vx-event/package.json | 1 + packages/vx-event/src/getXAndYFromEvent.ts | 13 +++++++------ packages/vx-event/src/localPoint.ts | 11 +++++------ packages/vx-event/src/localPointGeneric.ts | 3 ++- packages/vx-event/src/typeGuards.ts | 13 +++++++++---- packages/vx-event/src/types.ts | 1 + 6 files changed, 25 insertions(+), 17 deletions(-) create mode 100644 packages/vx-event/src/types.ts diff --git a/packages/vx-event/package.json b/packages/vx-event/package.json index 0e8cf0c85..8781393ae 100644 --- a/packages/vx-event/package.json +++ b/packages/vx-event/package.json @@ -31,6 +31,7 @@ "access": "public" }, "dependencies": { + "@types/react": "*", "@vx/point": "0.0.192" } } diff --git a/packages/vx-event/src/getXAndYFromEvent.ts b/packages/vx-event/src/getXAndYFromEvent.ts index ffaba0553..afdf120f6 100644 --- a/packages/vx-event/src/getXAndYFromEvent.ts +++ b/packages/vx-event/src/getXAndYFromEvent.ts @@ -1,6 +1,7 @@ +import { EventType } from './types'; import { isTouchEvent } from './typeGuards'; -export default function getXAndYFromEvent(event?: MouseEvent | TouchEvent) { +export default function getXAndYFromEvent(event?: EventType) { if (!event) return { x: 0, y: 0 }; if (isTouchEvent(event)) { @@ -8,10 +9,10 @@ export default function getXAndYFromEvent(event?: MouseEvent | TouchEvent) { x: event.changedTouches[0].clientX, y: event.changedTouches[0].clientX, }; - } else { - return { - x: event.clientX, - y: event.clientY, - }; } + + return { + x: event.clientX, + y: event.clientY, + }; } diff --git a/packages/vx-event/src/localPoint.ts b/packages/vx-event/src/localPoint.ts index 62ee8bc3a..9d1abeeb3 100644 --- a/packages/vx-event/src/localPoint.ts +++ b/packages/vx-event/src/localPoint.ts @@ -1,15 +1,14 @@ import localPointGeneric from './localPointGeneric'; +import { EventType } from './types'; import { isElement, isEvent } from './typeGuards'; -export default function localPoint( - nodeOrEvent: Element | MouseEvent | TouchEvent, - maybeEvent?: MouseEvent | TouchEvent, -) { +export default function localPoint(nodeOrEvent: Element | EventType, maybeEvent?: EventType) { if (isElement(nodeOrEvent) && maybeEvent) { return localPointGeneric(nodeOrEvent, maybeEvent); - } else if (isEvent(nodeOrEvent)) { + } + if (isEvent(nodeOrEvent)) { const node = nodeOrEvent.target as Element; if (node) return localPointGeneric(node, nodeOrEvent); } - return; + return undefined; } diff --git a/packages/vx-event/src/localPointGeneric.ts b/packages/vx-event/src/localPointGeneric.ts index 935bb9fb0..7cd173dde 100644 --- a/packages/vx-event/src/localPointGeneric.ts +++ b/packages/vx-event/src/localPointGeneric.ts @@ -1,8 +1,9 @@ import { Point } from '@vx/point'; +import { EventType } from './types'; import { isSVGElement, isSVGGraphicsElement, isSVGSVGElement } from './typeGuards'; import getXAndYFromEvent from './getXAndYFromEvent'; -export default function localPoint(node: Element, event: MouseEvent | TouchEvent) { +export default function localPoint(node: Element, event: EventType) { if (!node || !event) return; const coords = getXAndYFromEvent(event); diff --git a/packages/vx-event/src/typeGuards.ts b/packages/vx-event/src/typeGuards.ts index 4c1fd4a55..f316e075e 100644 --- a/packages/vx-event/src/typeGuards.ts +++ b/packages/vx-event/src/typeGuards.ts @@ -1,4 +1,6 @@ -export function isElement(elem?: Element | Event): elem is Element { +import { EventType } from './types'; + +export function isElement(elem?: Element | EventType): elem is Element { return !!elem && elem instanceof Element; } @@ -17,11 +19,14 @@ export function isSVGGraphicsElement(elem?: Element | null): elem is SVGGraphics } // functional definition of TouchEvent -export function isTouchEvent(event?: Event): event is TouchEvent { +export function isTouchEvent(event?: EventType): event is TouchEvent | React.TouchEvent { return !!event && 'changedTouches' in event; } // functional definition of event -export function isEvent(event?: Event | Element): event is Event { - return !!event && 'target' in event; +export function isEvent(event?: EventType | Element): event is EventType { + return ( + !!event && + (event instanceof Event || ('nativeEvent' in event && event.nativeEvent instanceof Event)) + ); } diff --git a/packages/vx-event/src/types.ts b/packages/vx-event/src/types.ts new file mode 100644 index 000000000..8ec87341d --- /dev/null +++ b/packages/vx-event/src/types.ts @@ -0,0 +1 @@ +export type EventType = MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent; From 83cf8d9f7f49e301b2213e4bf4cea1d8473a2bfc Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Tue, 17 Sep 2019 16:58:09 -0700 Subject: [PATCH 3/5] fix(vx-event/localPoint*): always return a Point --- packages/vx-event/src/localPoint.ts | 3 ++- packages/vx-event/src/localPointGeneric.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/vx-event/src/localPoint.ts b/packages/vx-event/src/localPoint.ts index 9d1abeeb3..f2c1b8e91 100644 --- a/packages/vx-event/src/localPoint.ts +++ b/packages/vx-event/src/localPoint.ts @@ -1,3 +1,4 @@ +import { Point } from '@vx/point'; import localPointGeneric from './localPointGeneric'; import { EventType } from './types'; import { isElement, isEvent } from './typeGuards'; @@ -10,5 +11,5 @@ export default function localPoint(nodeOrEvent: Element | EventType, maybeEvent? const node = nodeOrEvent.target as Element; if (node) return localPointGeneric(node, nodeOrEvent); } - return undefined; + return new Point({ x: 0, y: 0 }); } diff --git a/packages/vx-event/src/localPointGeneric.ts b/packages/vx-event/src/localPointGeneric.ts index 7cd173dde..2900ffedc 100644 --- a/packages/vx-event/src/localPointGeneric.ts +++ b/packages/vx-event/src/localPointGeneric.ts @@ -4,7 +4,7 @@ import { isSVGElement, isSVGGraphicsElement, isSVGSVGElement } from './typeGuard import getXAndYFromEvent from './getXAndYFromEvent'; export default function localPoint(node: Element, event: EventType) { - if (!node || !event) return; + if (!node || !event) return new Point({ x: 0, y: 0 }); const coords = getXAndYFromEvent(event); From c2a1a50da1cd079303fd2986a0ca6b5f81edcb88 Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Wed, 2 Oct 2019 13:24:15 -0700 Subject: [PATCH 4/5] typescript(vx-event/getXAndYFromEvent): add changedTouches length check --- packages/vx-event/src/getXAndYFromEvent.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/vx-event/src/getXAndYFromEvent.ts b/packages/vx-event/src/getXAndYFromEvent.ts index afdf120f6..8c95e49d6 100644 --- a/packages/vx-event/src/getXAndYFromEvent.ts +++ b/packages/vx-event/src/getXAndYFromEvent.ts @@ -1,14 +1,18 @@ import { EventType } from './types'; import { isTouchEvent } from './typeGuards'; +const DEFAULT_POINT = { x: 0, y: 0 }; + export default function getXAndYFromEvent(event?: EventType) { - if (!event) return { x: 0, y: 0 }; + if (!event) return { ...DEFAULT_POINT }; if (isTouchEvent(event)) { - return { - x: event.changedTouches[0].clientX, - y: event.changedTouches[0].clientX, - }; + return event.changedTouches.length > 0 + ? { + x: event.changedTouches[0].clientX, + y: event.changedTouches[0].clientX, + } + : { ...DEFAULT_POINT }; } return { From e5a0c80a81668d785e2726820a08c78ca015c479 Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Mon, 21 Oct 2019 20:49:43 -0700 Subject: [PATCH 5/5] new(localPoint, localPointGeneric): return null for empty events --- packages/vx-event/src/localPoint.ts | 3 +-- packages/vx-event/src/localPointGeneric.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/vx-event/src/localPoint.ts b/packages/vx-event/src/localPoint.ts index f2c1b8e91..3f497e108 100644 --- a/packages/vx-event/src/localPoint.ts +++ b/packages/vx-event/src/localPoint.ts @@ -1,4 +1,3 @@ -import { Point } from '@vx/point'; import localPointGeneric from './localPointGeneric'; import { EventType } from './types'; import { isElement, isEvent } from './typeGuards'; @@ -11,5 +10,5 @@ export default function localPoint(nodeOrEvent: Element | EventType, maybeEvent? const node = nodeOrEvent.target as Element; if (node) return localPointGeneric(node, nodeOrEvent); } - return new Point({ x: 0, y: 0 }); + return null; } diff --git a/packages/vx-event/src/localPointGeneric.ts b/packages/vx-event/src/localPointGeneric.ts index 2900ffedc..4138627c3 100644 --- a/packages/vx-event/src/localPointGeneric.ts +++ b/packages/vx-event/src/localPointGeneric.ts @@ -4,7 +4,7 @@ import { isSVGElement, isSVGGraphicsElement, isSVGSVGElement } from './typeGuard import getXAndYFromEvent from './getXAndYFromEvent'; export default function localPoint(node: Element, event: EventType) { - if (!node || !event) return new Point({ x: 0, y: 0 }); + if (!node || !event) return null; const coords = getXAndYFromEvent(event);