From ae27e2ce0fb021c1df2288c6e3680def1f70db19 Mon Sep 17 00:00:00 2001 From: Bowen Date: Fri, 3 May 2024 15:44:50 +0800 Subject: [PATCH] Return a more suitable ref (#5) Previously, the ref will always return an SVGGElement when the element's type is one of `path`, `circle`, `line`, `rect`, `ellipse`, `polygon`, `polyline`. Because we wrap these types of elements in an SVGGElement and return the SVGGElement as the ref. https://github.com/Bowen7/react-rough-fiber/blob/70f91aa7588ec8375dd53225d8ca74445a716f72/packages/react-rough-fiber/src/renderer.ts#L26-L32 Now, I have changed it to return the first path element of the SVGGElement. It's just a better, not a perfect way to handle the ref. Because `roughjs` renders all shapes to SVGPathElement, we can't get the original ref. --- packages/react-rough-fiber/package.json | 2 +- .../src/__tests__/ref.test.tsx | 45 +++++++++++++++++++ packages/react-rough-fiber/src/constants.ts | 2 + packages/react-rough-fiber/src/renderer.ts | 19 +++++--- 4 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 packages/react-rough-fiber/src/__tests__/ref.test.tsx diff --git a/packages/react-rough-fiber/package.json b/packages/react-rough-fiber/package.json index 7b7d0d5..9051aeb 100644 --- a/packages/react-rough-fiber/package.json +++ b/packages/react-rough-fiber/package.json @@ -1,6 +1,6 @@ { "name": "react-rough-fiber", - "version": "0.0.4", + "version": "0.0.5", "description": "A React renderer for rendering hand-drawn SVGs.", "keywords": [ "react", diff --git a/packages/react-rough-fiber/src/__tests__/ref.test.tsx b/packages/react-rough-fiber/src/__tests__/ref.test.tsx new file mode 100644 index 0000000..ec00628 --- /dev/null +++ b/packages/react-rough-fiber/src/__tests__/ref.test.tsx @@ -0,0 +1,45 @@ +import { cleanup, render, screen } from '@testing-library/react'; +import { RoughSVG } from '../index'; + +afterEach(() => { + cleanup(); + jest.clearAllMocks(); +}); + +describe('return correct ref', () => { + it('render g', () => { + let ref: SVGElement | null = null; + render( + + + + ref = _ref}/> + + + ); + expect(ref!.tagName).toBe('g'); + }); + + it('render path', () => { + let ref: SVGElement | null = null; + render( + + + ref = _ref} /> + + + + ); + expect(ref!.tagName).toBe('path'); + }); +}); diff --git a/packages/react-rough-fiber/src/constants.ts b/packages/react-rough-fiber/src/constants.ts index 990a7d4..ecc7711 100644 --- a/packages/react-rough-fiber/src/constants.ts +++ b/packages/react-rough-fiber/src/constants.ts @@ -61,3 +61,5 @@ export const SVG_SHAPE_MAP: { [key in SVGShape['type']]: SVGShape } = { export const FILL_CSS_VARIABLE = '--rrf-fill-color'; export const FILL_OPACITY_CSS_VARIABLE = '--rrf-fill-opacity'; + +export const DATA_RRF_GROUP = 'data-rrf-group'; \ No newline at end of file diff --git a/packages/react-rough-fiber/src/renderer.ts b/packages/react-rough-fiber/src/renderer.ts index 533c4d5..437b6aa 100644 --- a/packages/react-rough-fiber/src/renderer.ts +++ b/packages/react-rough-fiber/src/renderer.ts @@ -7,10 +7,9 @@ import { HostContext, HostConfig, InstanceWithListeners, - SVGShape, Options, } from './types'; -import { SVG_NAMESPACE, HTML_NAMESPACE, SVG_SHAPE_MAP } from './constants'; +import { SVG_NAMESPACE, HTML_NAMESPACE, SVG_SHAPE_MAP, DATA_RRF_GROUP } from './constants'; import { isFun } from './utils'; import { diffProps } from './props'; @@ -27,9 +26,11 @@ const createInstance = ( // roughjs renders a shape as a fill path(if fill is not none) and a stroke path(if stroke is not none) // so we need to wrap the shape in a g element if (!inDefs && type in SVG_SHAPE_MAP) { - type = 'g'; + domElement = ownerDocument.createElementNS(SVG_NAMESPACE, 'g'); + domElement.setAttribute(DATA_RRF_GROUP, ''); + } else { + domElement = ownerDocument.createElementNS(SVG_NAMESPACE, type); } - domElement = ownerDocument.createElementNS(SVG_NAMESPACE, type); } else { domElement = ownerDocument.createElement(type); } @@ -137,7 +138,15 @@ export const createReconciler = ( (textInstance).nodeValue = newText; }, commitMount() {}, - getPublicInstance: (instance) => instance!, + getPublicInstance: (instance) => { + if(instance?.hasAttribute(DATA_RRF_GROUP)) { + const firstChild = instance.children[0]; + if(firstChild?.tagName === 'path') { + return firstChild as SVGElement; + } + } + return instance! + }, prepareForCommit: () => null, preparePortalMount: () => {}, resetAfterCommit: () => {},