Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add react native to the list of useable options #305

Merged
merged 2 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading