Fixed desktop text selection behavior.
This commit is contained in:
21
src/App.test.tsx
Normal file
21
src/App.test.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import App from "./App";
|
||||
|
||||
vi.stubGlobal("Worker", class {
|
||||
postMessage() {}
|
||||
terminate() {}
|
||||
});
|
||||
|
||||
describe("App canvas selection", () => {
|
||||
it("does not expose a viewer canvas before a file is opened", () => {
|
||||
render(<App />);
|
||||
expect(screen.getByText("Open an Adobe XD file")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps normal document text non-selectable by CSS", () => {
|
||||
const { container } = render(<App />);
|
||||
fireEvent.doubleClick(container);
|
||||
expect(container.querySelector(".selectable-text")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
20
src/App.tsx
20
src/App.tsx
@@ -66,6 +66,13 @@ export default function App() {
|
||||
zoom: number;
|
||||
} | null>(null);
|
||||
const canvasRef = useRef<HTMLElement>(null);
|
||||
const selectedTextRef = useRef<SVGTextElement | null>(null);
|
||||
|
||||
const clearTextSelection = useCallback(() => {
|
||||
selectedTextRef.current?.classList.remove("selectable-text");
|
||||
selectedTextRef.current = null;
|
||||
window.getSelection()?.removeAllRanges();
|
||||
}, []);
|
||||
|
||||
useEffect(() => { panRef.current = pan; }, [pan]);
|
||||
useEffect(() => { zoomRef.current = zoom; }, [zoom]);
|
||||
@@ -243,6 +250,7 @@ export default function App() {
|
||||
}}
|
||||
onPointerDown={(event) => {
|
||||
if (event.pointerType === "touch") return;
|
||||
if (event.detail < 2) clearTextSelection();
|
||||
event.currentTarget.setPointerCapture(event.pointerId);
|
||||
dragRef.current = { x: event.clientX, y: event.clientY, originX: pan.x, originY: pan.y };
|
||||
}}
|
||||
@@ -257,6 +265,18 @@ export default function App() {
|
||||
onPointerCancel={(event) => {
|
||||
if (event.pointerType !== "touch") dragRef.current = null;
|
||||
}}
|
||||
onDoubleClick={(event) => {
|
||||
const target = event.target instanceof Element ? event.target.closest("text") : null;
|
||||
if (!(target instanceof SVGTextElement)) return;
|
||||
clearTextSelection();
|
||||
target.classList.add("selectable-text");
|
||||
selectedTextRef.current = target;
|
||||
const range = window.document.createRange();
|
||||
range.selectNodeContents(target);
|
||||
const selection = window.getSelection();
|
||||
selection?.removeAllRanges();
|
||||
selection?.addRange(range);
|
||||
}}
|
||||
>
|
||||
{document.artboards.length || document.pasteboardLayers.length ? (
|
||||
<div
|
||||
|
||||
@@ -61,11 +61,13 @@ h1 { margin: 0; font-size: 34px; letter-spacing: -1.3px; }
|
||||
.warnings li { padding: 8px 0; border-bottom: 1px solid #292c36; color: #aeb2bf; font-size: 11px; line-height: 1.4; }
|
||||
.warnings li small { display: block; color: #777c8b; }
|
||||
|
||||
.canvas { position: relative; display: grid; place-items: center; overflow: hidden; touch-action: none; overscroll-behavior: none; user-select: none; cursor: grab; background-color: #11131a; background-image: radial-gradient(#343743 1px, transparent 1px); background-size: 20px 20px; }
|
||||
.canvas { position: relative; display: grid; place-items: center; overflow: hidden; touch-action: none; overscroll-behavior: none; user-select: none; -webkit-user-select: none; cursor: grab; background-color: #11131a; background-image: radial-gradient(#343743 1px, transparent 1px); background-size: 20px 20px; }
|
||||
.canvas:active { cursor: grabbing; }
|
||||
.stage { position: relative; transform-origin: center; transition: transform .05s linear; }
|
||||
.document-stage { flex: 0 0 auto; }
|
||||
.document-view { display: block; overflow: visible; pointer-events: none; }
|
||||
.document-view { display: block; overflow: visible; }
|
||||
.document-view text { user-select: none; -webkit-user-select: none; cursor: default; }
|
||||
.document-view text.selectable-text { user-select: text; -webkit-user-select: text; cursor: text; }
|
||||
.document-artboard-label { fill: #969baa; font-size: 12px; }
|
||||
.artboard { display: block; overflow: visible; background: white; }
|
||||
.artboard-label { position: absolute; left: 0; top: -28px; max-width: 100%; color: #969baa; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
|
||||
Reference in New Issue
Block a user