54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
import { useState } from "react";
|
|
|
|
const COORD_RE = /\[(-?\d+),\s*(-?\d+)\]/g;
|
|
|
|
export function TextWithCoords({ text, style }: { text: string; style?: React.CSSProperties }) {
|
|
const parts: React.ReactNode[] = [];
|
|
let last = 0;
|
|
let match: RegExpExecArray | null;
|
|
|
|
COORD_RE.lastIndex = 0;
|
|
while ((match = COORD_RE.exec(text)) !== null) {
|
|
if (match.index > last) parts.push(text.slice(last, match.index));
|
|
parts.push(<CoordBadge key={match.index} x={match[1]} y={match[2]} raw={match[0]} />);
|
|
last = match.index + match[0].length;
|
|
}
|
|
if (last < text.length) parts.push(text.slice(last));
|
|
|
|
return <span style={style}>{parts}</span>;
|
|
}
|
|
|
|
function CoordBadge({ x, y, raw }: { x: string; y: string; raw: string }) {
|
|
const [copied, setCopied] = useState(false);
|
|
|
|
async function handleClick(e: React.MouseEvent) {
|
|
e.stopPropagation();
|
|
await navigator.clipboard.writeText(`/travel ${x},${y}`);
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 1500);
|
|
}
|
|
|
|
return (
|
|
<span
|
|
onClick={handleClick}
|
|
title={`Copier /travel ${x},${y}`}
|
|
style={{
|
|
display: "inline-block",
|
|
background: copied ? "rgba(74,222,128,0.15)" : "rgba(74,158,255,0.1)",
|
|
border: `1px solid ${copied ? "rgba(74,222,128,0.4)" : "rgba(74,158,255,0.3)"}`,
|
|
borderRadius: "3px",
|
|
padding: "0 5px",
|
|
color: copied ? "#4ade80" : "#93c5fd",
|
|
cursor: "pointer",
|
|
fontSize: "0.85em",
|
|
fontFamily: "monospace",
|
|
userSelect: "none",
|
|
transition: "background 0.15s, color 0.15s, border-color 0.15s",
|
|
verticalAlign: "baseline",
|
|
}}
|
|
>
|
|
{copied ? "✓ copié" : raw}
|
|
</span>
|
|
);
|
|
}
|