feat: implement SvgWithCss and SvgWithCssUri with Animated support

Expose types of SvgCss, SvgWithCss, SvgCssUri & SvgWithCssUri
Move css-select and css-tree to peerDependencies & devDependencies
This commit is contained in:
Mikael Sand
2019-10-23 19:13:57 +03:00
parent 7370225005
commit 6fb8dd53db
6 changed files with 143 additions and 27 deletions
+31 -11
View File
@@ -4183,7 +4183,8 @@
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
"dev": true
},
"bottleneck": {
"version": "2.19.5",
@@ -5188,6 +5189,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.2.tgz",
"integrity": "sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==",
"dev": true,
"requires": {
"boolbase": "^1.0.0",
"css-what": "^2.1.2",
@@ -5196,18 +5198,28 @@
}
},
"css-tree": {
"version": "1.0.0-alpha.36",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.36.tgz",
"integrity": "sha512-AFAFvEOaB8NPmPtSIy1wVyD6K1bC8YBc07BYTCmeB3EbQUqKBU/TUIbJGSgDWbrHPX1a6QsJExx7rd6mzagK0w==",
"version": "1.0.0-alpha.37",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
"integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==",
"dev": true,
"requires": {
"mdn-data": "2.0.4",
"source-map": "^0.5.3"
"source-map": "^0.6.1"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"css-what": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
"integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="
"integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
"dev": true
},
"cssom": {
"version": "0.3.8",
@@ -5529,6 +5541,7 @@
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz",
"integrity": "sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==",
"dev": true,
"requires": {
"domelementtype": "^2.0.1",
"entities": "^2.0.0"
@@ -5537,7 +5550,8 @@
"domelementtype": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
"integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ=="
"integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==",
"dev": true
}
}
},
@@ -5550,7 +5564,8 @@
"domelementtype": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
"integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
"integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
"dev": true
},
"domexception": {
"version": "1.0.1",
@@ -5565,6 +5580,7 @@
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
"integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
"dev": true,
"requires": {
"dom-serializer": "0",
"domelementtype": "1"
@@ -5661,7 +5677,8 @@
"entities": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
"integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
"integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==",
"dev": true
},
"env-ci": {
"version": "4.5.0",
@@ -10315,7 +10332,8 @@
"mdn-data": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
"integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA=="
"integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==",
"dev": true
},
"mem": {
"version": "4.3.0",
@@ -15825,6 +15843,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
"integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
"dev": true,
"requires": {
"boolbase": "~1.0.0"
}
@@ -18518,7 +18537,8 @@
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
},
"source-map-resolve": {
"version": "0.5.2",
+5 -4
View File
@@ -46,13 +46,12 @@
"semantic-release": "semantic-release"
},
"peerDependencies": {
"css-select": "^2.0.2",
"css-tree": "^1.0.0-alpha.37",
"react": "*",
"react-native": ">=0.50.0"
},
"dependencies": {
"css-select": "^2.0.2",
"css-tree": "^1.0.0-alpha.36"
},
"dependencies": {},
"devDependencies": {
"@react-native-community/bob": "^0.7.0",
"@react-native-community/eslint-config": "^0.0.5",
@@ -63,6 +62,8 @@
"@types/react-native": "^0.60.21",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",
"css-select": "^2.0.2",
"css-tree": "^1.0.0-alpha.37",
"eslint": "^6.5.1",
"eslint-plugin-flowtype": "^4.3.0",
"eslint-plugin-prettier": "^3.1.1",
+9 -1
View File
@@ -23,7 +23,13 @@ import Pattern, { RNSVGPattern } from './elements/Pattern';
import Mask, { RNSVGMask } from './elements/Mask';
import Marker, { RNSVGMarker } from './elements/Marker';
import { parse, SvgAst, SvgFromUri, SvgFromXml, SvgUri, SvgXml } from './xml';
import { SvgCss, SvgCssUri, inlineStyles } from './css';
import {
SvgCss,
SvgCssUri,
SvgWithCss,
SvgWithCssUri,
inlineStyles,
} from './css';
export {
Svg,
@@ -57,6 +63,8 @@ export {
SvgXml,
SvgCss,
SvgCssUri,
SvgWithCss,
SvgWithCssUri,
inlineStyles,
Shape,
RNSVGMarker,
+64 -3
View File
@@ -1,15 +1,18 @@
import React, { useEffect, useMemo, useState } from 'react';
import React, { Component, useEffect, useMemo, useState } from 'react';
import {
camelCase,
err,
fetchText,
JsxAST,
Middleware,
parse,
Styles,
SvgAst,
UriProps,
UriState,
XmlAST,
XmlProps,
XmlState,
} from './xml';
import csstree, {
Atrule,
@@ -595,7 +598,9 @@ const parseProps = {
* @author strarsis <strarsis@gmail.com>
* @author modified by: msand <msand@abo.fi>
*/
export function inlineStyles(document: XmlAST) {
export const inlineStyles: Middleware = function inlineStyles(
document: XmlAST,
) {
// collect <style/>s
const styleElements = querySelectorAll('style', document);
@@ -685,7 +690,7 @@ export function inlineStyles(document: XmlAST) {
}
return document;
}
};
export function SvgCss(props: XmlProps) {
const { xml, override } = props;
@@ -708,3 +713,59 @@ export function SvgCssUri(props: UriProps) {
}, [uri]);
return <SvgCss xml={xml} override={props} />;
}
// Extending Component is required for Animated support.
export class SvgWithCss extends Component<XmlProps, XmlState> {
state = { ast: null };
componentDidMount() {
this.parse(this.props.xml);
}
componentDidUpdate(prevProps: { xml: string | null }) {
const { xml } = this.props;
if (xml !== prevProps.xml) {
this.parse(xml);
}
}
parse(xml: string | null) {
try {
this.setState({ ast: xml ? parse(xml, inlineStyles) : null });
} catch (e) {
console.error(e);
}
}
render() {
const {
props,
state: { ast },
} = this;
return <SvgAst ast={ast} override={props.override || props} />;
}
}
export class SvgWithCssUri extends Component<UriProps, UriState> {
state = { xml: null };
componentDidMount() {
this.fetch(this.props.uri);
}
componentDidUpdate(prevProps: { uri: string | null }) {
const { uri } = this.props;
if (uri !== prevProps.uri) {
this.fetch(uri);
}
}
async fetch(uri: string | null) {
try {
this.setState({ xml: uri ? await fetchText(uri) : null });
} catch (e) {
console.error(e);
}
}
render() {
const {
props,
state: { xml },
} = this;
return <SvgWithCss xml={xml} override={props} />;
}
}
+31 -4
View File
@@ -446,13 +446,30 @@ export interface MaskProps extends CommonPathProps {
}
export const Mask: React.ComponentClass<MaskProps>;
export type Styles = { [property: string]: string };
export interface AST {
tag: string;
style?: Styles;
styles?: string;
priority?: Map<string, boolean | undefined>;
parent: AST | null;
children: (AST | string)[] | (JSX.Element | string)[];
props: {};
props: {
[prop: string]: Styles | string | undefined;
};
Tag: React.ComponentType;
}
export interface XmlAST extends AST {
children: (XmlAST | string)[];
parent: XmlAST | null;
}
export interface JsxAST extends AST {
children: (JSX.Element | string)[];
}
export interface UriProps extends SvgProps {
uri: string | null;
override?: SvgProps;
@@ -463,14 +480,16 @@ export interface XmlProps extends SvgProps {
xml: string | null;
override?: SvgProps;
}
export type XmlState = { ast: AST | null };
export type XmlState = { ast: JsxAST | null };
export interface AstProps extends SvgProps {
ast: AST | null;
ast: JsxAST | null;
override?: SvgProps;
}
export function parse(xml: string): AST | null;
export type Middleware = (ast: XmlAST) => XmlAST;
export function parse(source: string, middleware?: Middleware): JsxAST | null;
export const SvgAst: React.FunctionComponent<AstProps>;
@@ -479,3 +498,11 @@ export const SvgFromXml: React.ComponentClass<XmlProps, XmlState>;
export const SvgUri: React.FunctionComponent<UriProps>;
export const SvgFromUri: React.ComponentClass<UriProps, UriState>;
export const SvgCss: React.FunctionComponent<XmlProps>;
export const SvgWithCss: React.ComponentClass<XmlProps, XmlState>;
export const SvgCssUri: React.FunctionComponent<UriProps>;
export const SvgWithCssUri: React.ComponentClass<UriProps, UriState>;
export const inlineStyles: Middleware;
+3 -4
View File
@@ -263,10 +263,9 @@ const validNameCharacters = /[a-zA-Z0-9:_-]/;
const whitespace = /[\s\t\r\n]/;
const quotemarks = /['"]/;
export function parse(
source: string,
middleware?: (ast: XmlAST) => XmlAST,
): JsxAST | null {
export type Middleware = (ast: XmlAST) => XmlAST;
export function parse(source: string, middleware?: Middleware): JsxAST | null {
const length = source.length;
let currentElement: XmlAST | null = null;
let state = metadata;