Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Touch crosshair for line graphs #2524

Merged
merged 17 commits into from Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
plouc marked this conversation as resolved.
Show resolved Hide resolved
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(
plouc marked this conversation as resolved.
Show resolved Hide resolved
(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)