diff --git a/src/components/IconDetail.vue b/src/components/IconDetail.vue index 288dc387..1646d017 100644 --- a/src/components/IconDetail.vue +++ b/src/components/IconDetail.vue @@ -86,7 +86,7 @@ async function download(type: string) { const text = await getIconSnippet(props.icon, type, false, color.value) if (!text) return - const ext = (type === 'solid' || type === 'qwik') ? 'tsx' : type + const ext = (type === 'solid' || type === 'qwik' || type === 'react-native') ? 'tsx' : type const name = `${toComponentName(props.icon)}.${ext}` const blob = type === 'png' ? dataUrlToBlob(text) @@ -267,6 +267,9 @@ const collection = computed(() => { + @@ -313,6 +316,9 @@ const collection = computed(() => { +
diff --git a/src/utils/icons.ts b/src/utils/icons.ts index 6da6870b..b7c5eba5 100644 --- a/src/utils/icons.ts +++ b/src/utils/icons.ts @@ -133,6 +133,84 @@ ${svg.replace(//, '')} ` } +export function SvgToReactNative(svg: string, name: string, snippet: boolean) { + function replaceTags(svg: string, replacements: { + from: string + to: string + }[]): string { + let result = svg + replacements.forEach(({ from, to }) => { + result = result.replace(new RegExp(`<${from}(.*?)>`, 'g'), `<${to}$1>`) + .replace(new RegExp(``, 'g'), ``) + }) + return result + } + + function generateImports(usedComponents: string[]): string { + // Separate Svg from the other components + const svgIndex = usedComponents.indexOf('Svg') + if (svgIndex !== -1) + usedComponents.splice(svgIndex, 1) + + // Join all other component names with a comma and wrap them in curly braces + const componentsString = usedComponents.length > 0 ? `{ ${usedComponents.join(', ')} }` : '' + + // Return the consolidated import statement, ensuring Svg is imported as a default import + return `import Svg, ${componentsString} from 'react-native-svg';` + } + + const replacements: { + from: string + to: string + }[] = [ + { from: 'svg', to: 'Svg' }, + { from: 'path', to: 'Path' }, + { from: 'g', to: 'G' }, + { from: 'circle', to: 'Circle' }, + { from: 'rect', to: 'Rect' }, + { from: 'line', to: 'Line' }, + { from: 'polyline', to: 'Polyline' }, + { from: 'polygon', to: 'Polygon' }, + { from: 'ellipse', to: 'Ellipse' }, + { from: 'text', to: 'Text' }, + { from: 'tspan', to: 'Tspan' }, + { from: 'textPath', to: 'TextPath' }, + { from: 'defs', to: 'Defs' }, + { from: 'use', to: 'Use' }, + { from: 'symbol', to: 'Symbol' }, + { from: 'linearGradient', to: 'LinearGradient' }, + { from: 'radialGradient', to: 'RadialGradient' }, + { from: 'stop', to: 'Stop' }, + ] + + const reactNativeSvgCode = replaceTags(ClearSvg(svg, true), replacements) + .replace(/className=/g, '') + .replace(/href=/g, 'xlinkHref=') + .replace(/clip-path=/g, 'clipPath=') + .replace(/fill-opacity=/g, 'fillOpacity=') + .replace(/stroke-width=/g, 'strokeWidth=') + .replace(/stroke-linecap=/g, 'strokeLinecap=') + .replace(/stroke-linejoin=/g, 'strokeLinejoin=') + .replace(/stroke-miterlimit=/g, 'strokeMiterlimit=') + + const svgComponents = replacements.map(({ to }) => to) + const imports = generateImports(svgComponents.filter(component => reactNativeSvgCode.includes(component))) + + let code = ` +${imports} + +export function ${name}(props) { + return ( + ${reactNativeSvgCode} + ) +}` + + if (!snippet) + code = `import React from 'react';\n${code}\nexport default ${name}` + + return prettierCode(code, 'babel-ts') +} + export async function getIconSnippet(icon: string, type: string, snippet = true, color = 'currentColor'): Promise { if (!icon) return @@ -176,6 +254,8 @@ export async function getIconSnippet(icon: string, type: string, snippet = true, return SvgToSvelte(await getSvg(icon, undefined, color)) case 'astro': return SvgToAstro(await getSvg(icon, undefined, color)) + case 'react-native': + return SvgToReactNative(await getSvg(icon, undefined, color), toComponentName(icon), snippet) case 'unplugin': return `import ${toComponentName(icon)} from '~icons/${icon.split(':')[0]}/${icon.split(':')[1]}'` }