Skip to content

Instantly share code, notes, and snippets.

@tai-fukaya
Last active April 22, 2024 00:57
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 array = []
const codes = []
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}
console.log(style)
if (style['font-style'] === 'normal') {
delete after['font-style']
}
if ('font-size' in style) {
const pixel = new Number(style['font-size'].replace('px', ''))
after['font-size'] = `${pixel / 10}rem`
}
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 = new Number(percent[0].replace('%', '')) / 100
after['line-height'] = num
}
}
return after
}
const getCssTree = async (array, node) => {
if (node.visible === false) {
return
}
if (node.type === 'VECTOR') {
return
}
const style = await node.getCSSAsync()
const funcs = node.children?.map((node) => {
return (async () => {
return await getCssTree(array, node)
})()
})
const res = await Promise.all(funcs ?? [])
const sourceId = (node.type === 'TEXT' ? node.id: node.name)
const splitted = sourceId.split('->')
let id = 'N' + sourceId.replaceAll(/[ :;/()]+/g, '')
let tagName = node.type === 'TEXT' ? 'p' : 'div'
if (splitted.length > 1) {
id = splitted[1]
const splittedTagName = id.replace(')', '').split('(')
if (splittedTagName.length > 1) {
id = splittedTagName[0]
tagName = splittedTagName[1].toLowerCase()
}
}
return {
type: node.type,
id: id,
name: node.characters ?? node.name,
style: convertStyle(node, style),
tagName: tagName,
children: res.filter((node) => node)
}
}
// コンポーネントタグ生成
const genereateReactComponents = (node) => {
const template = node.type === 'TEXT' ? TEXT_TAG_TEMPLATE : TAG_TEMPLATE
let children = []
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
}
// styled-components生成
const generateStyledComponents = (node) => {
let array = []
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(node)
})
}
const funcs = selected.map((node) => {
return (async () => {
return await getCssTree(array, node)
})()
})
const res = await Promise.all(funcs)
// console.log(JSON.stringify(res, null, 2))
const children = []
res.forEach((node) => {
children.push(genereateReactComponents(node))
})
res.forEach((node) => {
generateStyledComponents(node)
})
const codeText = REACT_TEMPLATE.replace('%code%', children.join('')).replace('%styledCode%', codes.join(''))
console.log(codeText)
// async navigator.clipboard.writeText(codeText)
const children = figma.currentPage.children
let result = {
color: {},
background: {},
'font-weight': {},
'font-size': {},
'font-family': {},
}
const getCssTree = async (node, checkKey) => {
if (node.visible === false) {
return
}
if (node.type === 'VECTOR') {
return
}
const style = await node.getCSSAsync()
const funcs = node.children?.map((node) => {
return (async () => {
return await getCssTree(node, checkKey).catch((e) => console.log(e))
})()
})
await Promise.all(funcs ?? [])
if (checkKey in style) {
// console.log(style[checkKey])
result[checkKey][style[checkKey]] = (result[checkKey][style[checkKey]] ?? 0) + 1
}
}
const keys = Object.keys(result)
const funcs = children.map((node) => {
return keys.map((key) => {
return (async () => {
return await getCssTree(node, key)
})()
})
})
await Promise.all(funcs.flat())
console.log(JSON.stringify(result, null, 2))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment