From b4dc9756dffcb5cf830641a9c5e3b6145d89b5b7 Mon Sep 17 00:00:00 2001 From: Bohdan Artiukhov Date: Wed, 2 Oct 2024 09:23:34 +0200 Subject: [PATCH] feat: support css variables for SvgCss (#2459) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Summary Feature #2380 We want to add support for CSS variables when passing them to parse the SVG XML source function. ## Test Plan Test app -> src -> Test2380 ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ✅ | | MacOS | ✅ | | Android | ✅ | | Web | ✅ | --------- Co-authored-by: Jakub Grzywacz --- apps/test-examples/index.tsx | 1 + apps/test-examples/src/Test2380.tsx | 58 +++++++++++++++++++++ src/css/css.tsx | 80 ++++++++++++++++++++++++++++- 3 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 apps/test-examples/src/Test2380.tsx diff --git a/apps/test-examples/index.tsx b/apps/test-examples/index.tsx index d9afb126b..05d853456 100644 --- a/apps/test-examples/index.tsx +++ b/apps/test-examples/index.tsx @@ -27,6 +27,7 @@ import Test2327 from './src/Test2327'; import Test2233 from './src/Test2233'; import Test2363 from './src/Test2363'; import Test2366 from './src/Test2366'; +import Test2380 from './src/Test2380'; import Test2397 from './src/Test2397'; import Test2403 from './src/Test2403'; import Test2407 from './src/Test2407'; diff --git a/apps/test-examples/src/Test2380.tsx b/apps/test-examples/src/Test2380.tsx new file mode 100644 index 000000000..a1e0b74bf --- /dev/null +++ b/apps/test-examples/src/Test2380.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import {View} from 'react-native'; +import {SvgCss} from 'react-native-svg/css'; + +const xml = ` + + + + + + + + + + + + + + + Hello + + + + +`; + +export default function SvgComponent() { + return ( + + + + ); +} diff --git a/src/css/css.tsx b/src/css/css.tsx index b60a4c3c6..fba0e416c 100644 --- a/src/css/css.tsx +++ b/src/css/css.tsx @@ -592,6 +592,64 @@ const parseProps = { * @author strarsis * @author modified by: msand */ + +function extractVariables(stylesheet: CssNode): Map { + const variables = new Map(); + + csstree.walk(stylesheet, { + visit: 'Declaration', + enter(node) { + const { property, value } = node as Declaration; + if (property.startsWith('--')) { + const variableName = property.trim(); + const variableValue = csstree.generate(value).trim(); + variables.set(variableName, variableValue); + } + }, + }); + + return variables; +} + +function resolveVariables( + value: string | CssNode | undefined, + variables: Map +): string { + if (value === undefined) { + return ''; + } + const valueStr = typeof value === 'string' ? value : csstree.generate(value); + return valueStr.replace( + /var\((--[^,)]+)(?:,\s*([^)]+))?\)/g, + (_, variableName, fallback) => { + const resolvedValue = variables.get(variableName); + if (resolvedValue !== undefined) { + return resolveVariables(resolvedValue, variables); + } + return fallback ? resolveVariables(fallback, variables) : ''; + } + ); +} + +const propsToResolve = [ + 'color', + 'fill', + 'floodColor', + 'lightingColor', + 'stopColor', + 'stroke', +]; +const resolveElementVariables = ( + element: XmlAST, + variables: Map +) => + propsToResolve.forEach((prop) => { + const value = element.props[prop] as string; + if (value && value.startsWith('var(')) { + element.props[prop] = resolveVariables(value, variables); + } + }); + export const inlineStyles: Middleware = function inlineStyles( document: XmlAST ) { @@ -604,6 +662,7 @@ export const inlineStyles: Middleware = function inlineStyles( } const selectors: FlatSelectorList = []; + let variables = new Map(); for (const element of styleElements) { const { children } = element; @@ -615,7 +674,10 @@ export const inlineStyles: Middleware = function inlineStyles( // collect