Last active
April 22, 2024 00:57
-
-
Save tai-fukaya/fa6949b48f0e7fbc5e751c78f18b4fb0 to your computer and use it in GitHub Desktop.
Figmaの選択したフレームから、Reactコンポーネントを生成する
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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