fix(xml): extract tags map to separate entry point for mobile & web (#1916)

# Summary

**What issues does the pull request solve? Please tag them so that they
will get automatically closed once the PR is merged**

When utilizing with `react-native-web`: SvgXml and SvgUri are not
exported in the `src/ReactNativeSVG.web.ts` extension. The logic for
SvgXml and SvgUri do not have native dependencies, so they are safe to
consume on web.

Issues:
- https://github.com/software-mansion/react-native-svg/issues/1279
- https://github.com/software-mansion/react-native-svg/issues/1742

**What is the feature? (if applicable)**

Add SvgXml and SvgUri as consumable exports for `react-native-web`

**How did you implement the solution?**

Extract `tags` to shapes map into separate `tags.tsx` file, where native
shape elements and web shape elements can be provided to their
respective envs.

**What areas of the library does it impact?**

`src` directory

## Test Plan

Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes UI.

### What's required for testing (prerequisites)?
n/a

### What are the steps to reproduce (after prerequisites)?
n/a

## Compatibility

| OS      | Implemented |
| ------- | :---------: |
| iOS     |         |
| Android |         |

## Checklist

<!-- Check completed item, when applicable, via: [X] -->

- [x] I have tested this on a device and a simulator
- [x] I added documentation in `README.md`
- [x] I updated the typed files (typescript)
- [ ] I added a test for the API in the `__tests__` folder

---------

Co-authored-by: bohdanprog <bohdan.artiukhov@swmansion.com>
This commit is contained in:
Joshua Yoes
2024-07-29 04:06:19 -07:00
committed by GitHub
parent c5e601a98f
commit 360e0ee01a
9 changed files with 123 additions and 67 deletions

View File

@@ -17,7 +17,7 @@ export type FeColorMatrixProps = {
export default class FeColorMatrix extends FilterPrimitive<FeColorMatrixProps> { export default class FeColorMatrix extends FilterPrimitive<FeColorMatrixProps> {
static displayName = 'FeColorMatrix'; static displayName = 'FeColorMatrix';
static defaultProps = { static defaultProps: React.ComponentProps<typeof FeColorMatrix> = {
...this.defaultPrimitiveProps, ...this.defaultPrimitiveProps,
type: 'matrix', type: 'matrix',
values: '', values: '',

View File

@@ -20,7 +20,7 @@ export interface FeGaussianBlurProps {
export default class FeGaussianBlur extends FilterPrimitive<FeGaussianBlurProps> { export default class FeGaussianBlur extends FilterPrimitive<FeGaussianBlurProps> {
static displayName = 'FeGaussianBlur'; static displayName = 'FeGaussianBlur';
static defaultProps = { static defaultProps: React.ComponentProps<typeof FeGaussianBlur> = {
...this.defaultPrimitiveProps, ...this.defaultPrimitiveProps,
stdDeviation: 0, stdDeviation: 0,
edgeMode: 'none', edgeMode: 'none',

View File

@@ -14,7 +14,7 @@ export interface FeOffsetProps {
export default class FeOffset extends FilterPrimitive<FeOffsetProps> { export default class FeOffset extends FilterPrimitive<FeOffsetProps> {
static displayName = 'FeOffset'; static displayName = 'FeOffset';
static defaultProps = { static defaultProps: React.ComponentProps<typeof FeOffset> = {
...this.defaultPrimitiveProps, ...this.defaultPrimitiveProps,
dx: 0, dx: 0,
dy: 0, dy: 0,

View File

@@ -20,7 +20,7 @@ export interface FilterProps {
export default class Filter extends Shape<FilterProps> { export default class Filter extends Shape<FilterProps> {
static displayName = 'Filter'; static displayName = 'Filter';
static defaultProps = { static defaultProps: React.ComponentProps<typeof Filter> = {
x: '-10%', x: '-10%',
y: '-10%', y: '-10%',
width: '120%', width: '120%',

View File

@@ -16,7 +16,7 @@ export default class FilterPrimitive<P> extends Component<
[x: string]: unknown; [x: string]: unknown;
root: (FilterPrimitive<P> & NativeMethods) | null = null; root: (FilterPrimitive<P> & NativeMethods) | null = null;
static defaultPrimitiveProps = { static defaultPrimitiveProps: React.ComponentProps<typeof FilterPrimitive> = {
x: '0%', x: '0%',
y: '0%', y: '0%',
width: '100%', width: '100%',

View File

@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { tags } from '../../xml'; import { tags } from '../../tags';
import { FilterElement, Filters } from '../types'; import { FilterElement, Filters } from '../types';
import { parse } from './extractFiltersString'; import { parse } from './extractFiltersString';

57
src/tags.tsx Normal file
View File

@@ -0,0 +1,57 @@
import Rect from './elements/Rect';
import Circle from './elements/Circle';
import Ellipse from './elements/Ellipse';
import Polygon from './elements/Polygon';
import Polyline from './elements/Polyline';
import Line from './elements/Line';
import Svg from './elements/Svg';
import Path from './elements/Path';
import G from './elements/G';
import Text from './elements/Text';
import TSpan from './elements/TSpan';
import TextPath from './elements/TextPath';
import Use from './elements/Use';
import Image from './elements/Image';
import Symbol from './elements/Symbol';
import Defs from './elements/Defs';
import LinearGradient from './elements/LinearGradient';
import RadialGradient from './elements/RadialGradient';
import Stop from './elements/Stop';
import ClipPath from './elements/ClipPath';
import Pattern from './elements/Pattern';
import Mask from './elements/Mask';
import Marker from './elements/Marker';
import Filter from './elements/filters/Filter';
import FeColorMatrix from './elements/filters/FeColorMatrix';
import FeGaussianBlur from './elements/filters/FeGaussianBlur';
import FeOffset from './elements/filters/FeOffset';
export const tags = {
circle: Circle,
clipPath: ClipPath,
defs: Defs,
ellipse: Ellipse,
filter: Filter,
feColorMatrix: FeColorMatrix,
feGaussianBlur: FeGaussianBlur,
feOffset: FeOffset,
g: G,
image: Image,
line: Line,
linearGradient: LinearGradient,
marker: Marker,
mask: Mask,
path: Path,
pattern: Pattern,
polygon: Polygon,
polyline: Polyline,
radialGradient: RadialGradient,
rect: Rect,
stop: Stop,
svg: Svg,
symbol: Symbol,
text: Text,
textPath: TextPath,
tspan: TSpan,
use: Use,
} as const;

50
src/tags.web.tsx Normal file
View File

@@ -0,0 +1,50 @@
import Svg, {
Circle,
ClipPath,
Defs,
Ellipse,
G,
Image,
Line,
LinearGradient,
Marker,
Mask,
Path,
Pattern,
Polygon,
Polyline,
RadialGradient,
Rect,
Stop,
Text,
TextPath,
TSpan,
Use,
Symbol,
} from './ReactNativeSVG.web';
export const tags = {
circle: Circle,
clipPath: ClipPath,
defs: Defs,
ellipse: Ellipse,
g: G,
image: Image,
line: Line,
linearGradient: LinearGradient,
marker: Marker,
mask: Mask,
path: Path,
pattern: Pattern,
polygon: Polygon,
polyline: Polyline,
radialGradient: RadialGradient,
rect: Rect,
stop: Stop,
svg: Svg,
symbol: Symbol,
text: Text,
textPath: TextPath,
tspan: TSpan,
use: Use,
} as const;

View File

@@ -1,69 +1,14 @@
import type { ComponentType } from 'react'; import type { ComponentType, ComponentProps } from 'react';
import * as React from 'react'; import * as React from 'react';
import { Component, useEffect, useMemo, useState } from 'react'; import { Component, useEffect, useMemo, useState } from 'react';
import Rect from './elements/Rect';
import Circle from './elements/Circle';
import Ellipse from './elements/Ellipse';
import Polygon from './elements/Polygon';
import Polyline from './elements/Polyline';
import Line from './elements/Line';
import type { SvgProps } from './elements/Svg'; import type { SvgProps } from './elements/Svg';
import Svg from './elements/Svg'; import { tags } from './tags';
import Path from './elements/Path';
import G from './elements/G';
import Text from './elements/Text';
import TSpan from './elements/TSpan';
import TextPath from './elements/TextPath';
import Use from './elements/Use';
import Image from './elements/Image';
import Symbol from './elements/Symbol';
import Defs from './elements/Defs';
import LinearGradient from './elements/LinearGradient';
import RadialGradient from './elements/RadialGradient';
import Stop from './elements/Stop';
import ClipPath from './elements/ClipPath';
import Pattern from './elements/Pattern';
import Mask from './elements/Mask';
import Marker from './elements/Marker';
import Filter from './elements/filters/Filter';
import FeColorMatrix from './elements/filters/FeColorMatrix';
import FeGaussianBlur from './elements/filters/FeGaussianBlur';
import FeOffset from './elements/filters/FeOffset';
export const tags: { [tag: string]: ComponentType } = {
svg: Svg,
circle: Circle,
ellipse: Ellipse,
g: G,
text: Text,
tspan: TSpan,
textPath: TextPath,
path: Path,
polygon: Polygon,
polyline: Polyline,
line: Line,
rect: Rect,
use: Use,
image: Image,
symbol: Symbol,
defs: Defs,
linearGradient: LinearGradient,
radialGradient: RadialGradient,
stop: Stop,
clipPath: ClipPath,
pattern: Pattern,
mask: Mask,
marker: Marker,
filter: Filter,
feColorMatrix: FeColorMatrix,
feGaussianBlur: FeGaussianBlur,
feOffset: FeOffset,
};
function missingTag() { function missingTag() {
return null; return null;
} }
type Tag = ComponentType<ComponentProps<(typeof tags)[keyof typeof tags]>>;
export interface AST { export interface AST {
tag: string; tag: string;
style?: Styles; style?: Styles;
@@ -74,7 +19,7 @@ export interface AST {
props: { props: {
[prop: string]: Styles | string | undefined; [prop: string]: Styles | string | undefined;
}; };
Tag: ComponentType<React.PropsWithChildren>; Tag: Tag;
} }
export interface XmlAST extends AST { export interface XmlAST extends AST {
@@ -106,6 +51,9 @@ export function SvgAst({ ast, override }: AstProps) {
return null; return null;
} }
const { props, children } = ast; const { props, children } = ast;
const Svg = tags.svg;
return ( return (
<Svg {...props} {...override}> <Svg {...props} {...override}>
{children} {children}
@@ -385,14 +333,14 @@ export function parse(source: string, middleware?: Middleware): JsxAST | null {
return closingTag; return closingTag;
} }
const tag = getName(); const tag = getName() as keyof typeof tags;
const props: { [prop: string]: Styles | string | undefined } = {}; const props: { [prop: string]: Styles | string | undefined } = {};
const element: XmlAST = { const element: XmlAST = {
tag, tag,
props, props,
children: [], children: [],
parent: currentElement, parent: currentElement,
Tag: tags[tag] || missingTag, Tag: (tags[tag] || missingTag) as Tag,
}; };
if (currentElement) { if (currentElement) {
@@ -594,3 +542,4 @@ export function parse(source: string, middleware?: Middleware): JsxAST | null {
return null; return null;
} }
export { tags };