Skip to content

Instantly share code, notes, and snippets.

@tai-fukaya
Last active February 19, 2025 05:28
Show Gist options
  • Save tai-fukaya/fa6949b48f0e7fbc5e751c78f18b4fb0 to your computer and use it in GitHub Desktop.
Save tai-fukaya/fa6949b48f0e7fbc5e751c78f18b4fb0 to your computer and use it in GitHub Desktop.
Figmaの選択したフレームから、Reactコンポーネントを生成する
const selected = figma.currentPage.selection
const REACT_TEMPLATE = `
'use client'
import styled from 'styled-components'
const Component = () => {
return (
<>
%code%
</>
)
}
export default Component
%styledCode%
`
const TEXT_TAG_TEMPLATE = `
<%id%>%name%</%id%>
`
const TAG_TEMPLATE = `
<%id%>%children%</%id%>
`
const STYLED_COMPONENTS_TEMPLATE = `
const %id% = styled.%tag%\`
%style%
\`
`
const convertStyle = (node, style) => {
const after = { ...style }
if (style['font-style'] === 'normal') {
delete after['font-style']
}
if ('color' in style) {
after['color'] = style['color'].replace(/var\(.+, (#[a-fA-F0-9]+)\)/, '$1')
}
if ('background' in style) {
after['background'] = style['background'].replace(
/var\(.+, (#[a-fA-F0-9]+)\)/,
'$1'
)
}
if ('line-height' in style) {
const percent = style['line-height'].match(/\d+\.{0,1}\d*%/)
if (percent?.length > 0) {
const num = parseFloat(percent[0].replace('%', '')) / 100
after['line-height'] = num
}
}
return after
}
const getTree = async (node) => {
if (node.visible === false) {
return
}
if (node.type === 'VECTOR') {
return
}
const style = await node.getCSSAsync()
let children: any[] = []
if (node.children) {
for (let child of node.children) {
const res = await getTree(child)
children.push(res)
}
}
const sourceId = node.type === 'TEXT' ? node.id : node.name
let id = 'N' + sourceId.replaceAll(/[ :;/()]+/g, '')
let tagName = node.type === 'TEXT' ? 'p' : 'div'
if (
node.fills &&
node.fills.length > 0 &&
node.fills.find((fill) => fill.type === 'IMAGE')
) {
tagName = 'img'
}
return {
type: node.type,
id: id,
name: node.characters ?? node.name,
style: convertStyle(node, style),
tagName: tagName,
children: children.filter((node) => node),
}
}
const genereateReactComponents = (node) => {
const template = node.type === 'TEXT' ? TEXT_TAG_TEMPLATE : TAG_TEMPLATE
let children: string[] = []
node.children.forEach((node) => {
children.push(genereateReactComponents(node))
})
const code = template
.replaceAll('%id%', node.id)
.replaceAll('%name%', node.name.replaceAll('\n', '<br />'))
.replaceAll('%children%', children.join(''))
return code
}
const generateStyledComponents = (codes, node) => {
let array: string[] = []
for (let k in node.style) {
array.push(`${k}: ${node.style[k]}`)
}
const code = STYLED_COMPONENTS_TEMPLATE.replaceAll('%id%', node.id)
.replaceAll('%tag%', node.tagName)
.replaceAll('%style%', [...array, ''].join(';\n'))
codes.push(code)
node.children.forEach((node) => {
generateStyledComponents(codes, node)
})
}
let children: any[] = []
const reactCodes: string[] = []
const styledComponentsCodes: string[] = []
for (let node of selected) {
const res = await getTree(node)
children.push(res)
}
children.forEach((child) => {
reactCodes.push(genereateReactComponents(child))
})
children.forEach((child) => {
generateStyledComponents(styledComponentsCodes, child)
})
const codeText = REACT_TEMPLATE.replace('%code%', reactCodes.join('')).replace(
'%styledCode%',
styledComponentsCodes.join('')
)
print(codeText)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment