React でSVGベースのアプリを作ってて、途中で inline に input 要素を置きたかったがSVGにはそういうインタラクティブな要素がない。座標計算してポップアップを置いてもいいが、だるい。
と思っていろいろ考えてたが、svg には inline で HTML を埋め込める foreignObject という要素があるのを思い出した。
React の SVG 要素でなんやかんややって値を取る
<svg width={800} height={600}>
<g transform="translate(60,10)">
<foreignObject
x={0}
y={0}
width="110"
height="32"
style={{ background: "red" }}
>
<input
style={{ width: 100 }}
defaultValue="10px"
onChange={ev => {
console.log("value", ev.target.value);
}}
/>
</foreignObject>
</g>
</svg>;
最終的にこうなった
type InlineInputProps = {
width: number;
height: number;
value: string;
onChange: (ev: any) => void;
};
class InlineInput extends React.Component<
InlineInputProps,
{
value: string;
}
> {
constructor(props: InlineInputProps) {
super(props);
this.state = {
value: this.props.value
};
}
render() {
return (
<foreignObject width={this.props.width} height={this.props.height}>
<input
style={{
padding: 0,
margin: 0,
width: this.props.width,
height: this.props.height,
outline: "none",
boxSizing: "border-box"
}}
value={this.state.value}
onChange={ev => {
this.props.onChange(ev);
this.setState({ value: ev.target.value });
}}
/>
</foreignObject>
);
}
}