No overload matches for this call
For props x, y in StyledContainer
, it's throwing "No overload matches for this call".
import React from "react";
import styled, { css } from "styled-components";
interface Props {
onDragStart?: () => void;
onDragEnd?: () => void;
children: React.ReactNode;
isDragging: boolean;
x?: number;
y?: number;
onDrag?: (position: { translateX: number; translateY: number }) => void;
}
interface State {
originalX: number;
originalY: number;
lastTranslateX: number;
lastTranslateY: number;
translateX: number;
translateY: number;
isDragging: boolean;
}
export default class EventRecap extends React.Component<Props, State> {
state = {
isDragging: false,
originalX: 0,
originalY: 0,
translateX: 0,
translateY: 0,
lastTranslateX: 0,
lastTranslateY: 0,
};
componentWillUnmount() {
window.removeEventListener("mousemove", this.handleMouseMove);
window.removeEventListener("mouseup", this.handleMouseUp);
}
handleMouseDown = ({
clientX,
clientY,
}: {
clientX: number;
clientY: number;
}) => {
window.addEventListener("mousemove", this.handleMouseMove);
window.addEventListener("mouseup", this.handleMouseUp);
if (this.props.onDragStart) {
this.props.onDragStart();
}
this.setState({
originalX: clientX,
originalY: clientY,
isDragging: true,
});
};
handleMouseMove = ({
clientX,
clientY,
}: {
clientX: number;
clientY: number;
}) => {
const { isDragging } = this.state;
const { onDrag } = this.props;
if (!isDragging) {
return;
}
this.setState(
(prevState) => ({
translateX: clientX - prevState.originalX prevState.lastTranslateX,
translateY: clientY - prevState.originalY prevState.lastTranslateY,
}),
() => {
if (onDrag) {
onDrag({
translateX: this.state.translateX,
translateY: this.state.translateY,
});
}
},
);
};
handleMouseUp = () => {
window.removeEventListener("mousemove", this.handleMouseMove);
window.removeEventListener("mouseup", this.handleMouseUp);
this.setState(
{
originalX: 0,
originalY: 0,
lastTranslateX: this.state.translateX,
lastTranslateY: this.state.translateY,
isDragging: false,
},
() => {
if (this.props.onDragEnd) {
this.props.onDragEnd();
}
},
);
};
render() {
const { children } = this.props;
const { translateX, translateY, isDragging } = this.state;
return (
<StyledContainer
x={translateX}
y={translateY}
isDragging={isDragging}
onm ouseDown={this.handleMouseDown}
>
{children}
</StyledContainer>
);
}
}
const StyledContainer = styled.div.attrs<Props>((props) => ({
style: { transform: `translate(${props.x}px, ${props.y}px)` },
}))`
cursor: grab;
${({ isDragging }: { isDragging: boolean }) =>
isDragging &&
css`
opacity: 0.8;
cursor: grabbing;
`};
`;
CodePudding user response:
Instead of using attributes merging with styled.div.attrs<Props>
, directly use custom props so that they are exposed as the styled component props definition as well:
// Add the custom Props as a TypeScript type argument to styled.div
// https://styled-components.com/docs/api#using-custom-props
const StyledContainer2 = styled.div<Props>`
cursor: grab;
/* Directly adapt style based on props, instead of merging attributes
https://styled-components.com/docs/basics#adapting-based-on-props */
transform: ${(props) => css`translate(${props.x}px, ${props.y}px)`};
${({
// No need to redefine type, it is directly taken from the custom props type argument of styled.div
isDragging
}) =>
isDragging
? css`
opacity: 0.8;
cursor: grabbing;
`
: ""};
`;
Then you can use these props directly inside the style block and adapt style based on props (instead of the attributes merging).
Another advantage is that the props typing is directly available when you interpolate your function, so there is no need to redefine the props type.
Demo: https://codesandbox.io/s/laughing-rgb-oomc6n?file=/src/App.tsx:330-972