Skip to content

Commit

Permalink
feat(line): add support for touch events + crosshair (#2524)
Browse files Browse the repository at this point in the history
* Fix build + note Windows development

* Add touch cursor support

* Make touch crosshair optional + off by default

* `touchCrosshair` > `enableTouchCrosshair`

* Add missing hook dependencies

* Make unions of MouseEvent | TouchEvent

* Added website docs

* Update help

* Add tests

* Add touch crosshair to storybook

* Fix make command

* Add support for touch crosshair on slice based graphs

* Update docs

* Tweak defaults

* Added note about slices vs useMesh for line isInteractive

* Fix missing import

* Tweaks comments
  • Loading branch information
WilliamABradley committed Mar 5, 2024
1 parent d74996a commit a90a6cc
Show file tree
Hide file tree
Showing 21 changed files with 457 additions and 52 deletions.
6 changes: 5 additions & 1 deletion CONTRIBUTING.md
Expand Up @@ -28,7 +28,11 @@ the various packages, please execute the following:
make init
```

> please note that it will take a while as this project uses a lot of dependencies…
> please note that it will take a while as this project uses a lot of dependencies…'
### Windows

If you want to build this project on Windows, it is recommended to use either WSL 2, or Git bash + `choco install make`.

## Development

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -78,7 +78,7 @@ fmt-check: ##@0 global check if files were all formatted using prettier

test: ##@0 global run all checks/tests (packages, website)
@$(MAKE) fmt-check
@$(MAKE) lint
@$(MAKE) pkgs-lint
@$(MAKE) pkgs-test

vercel-build: ##@0 global Build the website and storybook to vercel
Expand Down
9 changes: 6 additions & 3 deletions package.json
Expand Up @@ -18,6 +18,9 @@
"keywords": [],
"devDependencies": {
"@babel/core": "^7.21.5",
"@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.5",
"@ekino/config": "^0.3.0",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-node-resolve": "^15.0.2",
Expand All @@ -27,6 +30,7 @@
"@types/lodash": "^4.14.170",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@types/react-test-renderer": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1",
"@wojtekmaj/enzyme-adapter-react-17": "0.6.6",
Expand All @@ -35,6 +39,7 @@
"babel-loader": "^8.2.3",
"chalk": "^5.2.0",
"chalk-template": "^1.0.0",
"cypress": "^12.11.0",
"enzyme": "^3.11.0",
"eslint": "^8.39.0",
"eslint-config-prettier": "^8.8.0",
Expand All @@ -57,16 +62,14 @@
"react": "^18.0.2",
"react-dom": "^18.0.2",
"react-test-renderer": "^18.2.0",
"@types/react-test-renderer": "^18.0.0",
"resize-observer-polyfill": "^1.5.1",
"rollup": "^3.21.0",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-size": "^0.3.1",
"rollup-plugin-strip-banner": "^3.0.0",
"rollup-plugin-visualizer": "^5.5.2",
"serve": "^13.0.2",
"typescript": "^4.9.5",
"cypress": "^12.11.0"
"typescript": "^4.9.5"
},
"resolutions": {
"@types/react": "^18.2.0",
Expand Down
5 changes: 4 additions & 1 deletion packages/core/index.d.ts
Expand Up @@ -516,7 +516,10 @@ export function usePropertyAccessor<Datum, Value>(
accessor: PropertyAccessor<Datum, Value>
): (datum: Datum) => Value

export function getRelativeCursor(element: Element, event: React.MouseEvent): [number, number]
export function getRelativeCursor(
element: Element,
event: React.MouseEvent | React.TouchEvent
): [number, number]
export function isCursorInRect(
x: number,
y: number,
Expand Down
9 changes: 6 additions & 3 deletions packages/core/src/lib/interactivity/index.js
Expand Up @@ -22,7 +22,8 @@ export * from './detect'
* give us the scaling factor to calculate the proper mouse position.
*/
export const getRelativeCursor = (el, event) => {
const { clientX, clientY } = event
const { clientX, clientY } = 'touches' in event ? event.touches[0] : event

// Get the dimensions of the element, in case it has
// been scaled using a transform for example, we get
// the scaled dimensions, not the original ones.
Expand All @@ -36,8 +37,10 @@ export const getRelativeCursor = (el, event) => {
} else {
// Other elements.
originalBox = {
width: el.offsetWidth,
height: el.offsetHeight,
// These should be here, except when we are running in jsdom.
// https://github.com/jsdom/jsdom/issues/135
width: el.offsetWidth || 0,
height: el.offsetHeight || 0,
}
}

Expand Down
5 changes: 5 additions & 0 deletions packages/line/index.d.ts
Expand Up @@ -99,6 +99,7 @@ export interface Point {
export type AccessorFunc = (datum: Point['data']) => string

export type PointMouseHandler = (point: Point, event: React.MouseEvent) => void
export type PointTouchHandler = (point: Point, event: React.TouchEvent) => void

export interface PointTooltipProps {
point: Point
Expand Down Expand Up @@ -185,6 +186,9 @@ export interface LineProps {
onMouseMove?: PointMouseHandler
onMouseLeave?: PointMouseHandler
onClick?: PointMouseHandler
onTouchStart?: PointTouchHandler
onTouchMove?: PointTouchHandler
onTouchEnd?: PointTouchHandler

debugMesh?: boolean

Expand All @@ -197,6 +201,7 @@ export interface LineProps {

enableCrosshair?: boolean
crosshairType?: CrosshairType
enableTouchCrosshair?: boolean

legends?: LegendProps[]
}
Expand Down
11 changes: 11 additions & 0 deletions packages/line/src/Line.js
Expand Up @@ -101,6 +101,9 @@ const Line = props => {
onMouseMove,
onMouseLeave,
onClick,
onTouchStart,
onTouchMove,
onTouchEnd,

tooltip = PointTooltip,

Expand All @@ -110,6 +113,7 @@ const Line = props => {

enableCrosshair = true,
crosshairType = 'bottom-left',
enableTouchCrosshair = false,

role = 'img',
} = props
Expand Down Expand Up @@ -241,6 +245,9 @@ const Line = props => {
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
onClick={onClick}
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
/>
)
}
Expand Down Expand Up @@ -303,7 +310,11 @@ const Line = props => {
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
onClick={onClick}
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
tooltip={tooltip}
enableTouchCrosshair={enableTouchCrosshair}
debug={debugMesh}
/>
)
Expand Down
48 changes: 47 additions & 1 deletion packages/line/src/Mesh.js
Expand Up @@ -21,8 +21,12 @@ const Mesh = ({
onMouseMove,
onMouseLeave,
onClick,
onTouchStart,
onTouchMove,
onTouchEnd,
tooltip,
debug,
enableTouchCrosshair,
}) => {
const { showTooltipAt, hideTooltip } = useTooltip()

Expand All @@ -49,7 +53,7 @@ const Mesh = ({
setCurrent(point)
onMouseMove && onMouseMove(point, event)
},
[setCurrent, showTooltipAt, tooltip, onMouseMove]
[showTooltipAt, tooltip, margin.left, margin.top, setCurrent, onMouseMove]
)

const handleMouseLeave = useCallback(
Expand All @@ -68,6 +72,41 @@ const Mesh = ({
[onClick]
)

const handleTouchStart = useCallback(
(point, event) => {
showTooltipAt(
createElement(tooltip, { point }),
[point.x + margin.left, point.y + margin.top],
'top'
)
setCurrent(point)
onTouchStart && onTouchStart(point, event)
},
[margin.left, margin.top, onTouchStart, setCurrent, showTooltipAt, tooltip]
)

const handleTouchMove = useCallback(
(point, event) => {
showTooltipAt(
createElement(tooltip, { point }),
[point.x + margin.left, point.y + margin.top],
'top'
)
setCurrent(point)
onTouchMove && onTouchMove(point, event)
},
[margin.left, margin.top, onTouchMove, setCurrent, showTooltipAt, tooltip]
)

const handleTouchEnd = useCallback(
(point, event) => {
hideTooltip()
setCurrent(null)
onTouchEnd && onTouchEnd(point, event)
},
[onTouchEnd, hideTooltip, setCurrent]
)

return (
<BaseMesh
nodes={points}
Expand All @@ -77,6 +116,10 @@ const Mesh = ({
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
onClick={handleClick}
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
enableTouchCrosshair={enableTouchCrosshair}
debug={debug}
/>
)
Expand All @@ -92,6 +135,9 @@ Mesh.propTypes = {
onMouseMove: PropTypes.func,
onMouseLeave: PropTypes.func,
onClick: PropTypes.func,
onTouchStart: PropTypes.func,
onTouchMove: PropTypes.func,
onTouchEnd: PropTypes.func,
tooltip: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired,
debug: PropTypes.bool.isRequired,
}
Expand Down
10 changes: 10 additions & 0 deletions packages/line/src/Slices.js
Expand Up @@ -22,11 +22,15 @@ const Slices = ({
onMouseMove,
onMouseLeave,
onClick,
onTouchStart,
onTouchMove,
onTouchEnd,
}) => {
return slices.map(slice => (
<SlicesItem
key={slice.id}
slice={slice}
slices={slices}
axis={axis}
debug={debug}
height={height}
Expand All @@ -37,6 +41,9 @@ const Slices = ({
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
onClick={onClick}
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
/>
))
}
Expand Down Expand Up @@ -64,6 +71,9 @@ Slices.propTypes = {
onMouseMove: PropTypes.func,
onMouseLeave: PropTypes.func,
onClick: PropTypes.func,
onTouchStart: PropTypes.func,
onTouchMove: PropTypes.func,
onTouchEnd: PropTypes.func,
}

export default memo(Slices)

0 comments on commit a90a6cc

Please sign in to comment.