Skip to content

Commit

Permalink
feat: add react native to the list of useable options (#305)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <[email protected]>
  • Loading branch information
mzaien and antfu authored Apr 12, 2024
1 parent 64d0e9c commit a5a9a0f
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 1 deletion.
8 changes: 7 additions & 1 deletion src/components/IconDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -267,6 +267,9 @@ const collection = computed(() => {
<button class="btn small mr-1 mb-1 opacity-75" @click="copy('astro')">
Astro
</button>
<button class="btn small mr-1 mb-1 opacity-75" @click="copy('react-native')">
React Native
</button>
<button class="btn small mr-1 mb-1 opacity-75" @click="copy('unplugin')">
Unplugin Icons
</button>
Expand Down Expand Up @@ -313,6 +316,9 @@ const collection = computed(() => {
<button class="btn small mr-1 mb-1 opacity-75" @click="download('astro')">
Astro
</button>
<button class="btn small mr-1 mb-1 opacity-75" @click="download('react-native')">
React Native
</button>
</div>
<div class="mr-4">
<div class="my-1 text-gray-500 text-sm">
Expand Down
80 changes: 80 additions & 0 deletions src/utils/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,84 @@ ${svg.replace(/<svg (.*?)>/, '<svg $1 {...props}>')}
`
}

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(`</${from}>`, 'g'), `</${to}>`)
})
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<string | undefined> {
if (!icon)
return
Expand Down Expand Up @@ -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]}'`
}
Expand Down

0 comments on commit a5a9a0f

Please sign in to comment.