Skip to content

Commit

Permalink
Merge pull request #22 from makamekm/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
makamekm authored Apr 29, 2020
2 parents b0ac5ee + e8cb077 commit c065c8c
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 205 deletions.
242 changes: 238 additions & 4 deletions content.plugins.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
const { emptyChildren, getComponentName, createComponent, getDescriptionStyles, saveSvgToDisk } = require('./lib');
const {
colorString,
emptyChildren,
getComponentName,
createComponent,
getDescriptionStyles,
saveSvgToDisk,
getPaint,
applyFontStyle,
dropShadow,
innerShadow,
clearObject
} = require('./lib');
const svgtojsx = require('./svg-to-jsx');

const contentPlugins = [
Expand All @@ -12,7 +24,10 @@ const contentPlugins = [
renderIfFalse,
setOnClick,
setId,
addClassName
addClassName,
addValue,
addOnChange,
setTextRenderer
];

function applyStyles(state) {
Expand All @@ -29,7 +44,7 @@ async function setComponentFromCache(state, shared) {
if (node.id !== component.id && node.name.charAt(0) === '#') {
const name = getComponentName(node.name, options);
emptyChildren(state);
content.push(`<${name} {...props} nodeId='${node.id}' />`);
content.push(`<${name} {...props} />`);
if (!componentMap[name]) await createComponent(node, shared);
localComponentMap[name] = componentMap[name];

Expand Down Expand Up @@ -157,6 +172,222 @@ function addClassName(state) {
classNames.push(props.class);
}
}
function addValue(state, { props: componentProps }) {
const { props, nodeProps } = state;
if (Object.keys(props).includes('value')) {
const args = props.value.split('.');
const value = args[0];
const elementType = args[1];
nodeProps['value'] = value;
componentProps[value] = `${elementType || 'any'}`;
}
}

function addOnChange(state, { props: componentProps }) {
const { props, nodeProps } = state;
if (Object.keys(props).includes('onChange')) {
const args = props.onChange.split('.');
const value = args[0];
const elementType = args[1];
nodeProps['onChange'] = value;
componentProps[value] = `React.ChangeEventHandler<${elementType || 'any'}>`;
}
}

function setTextRenderer({ node, props, middleStyle, content, nodeProps, classNames }, { printStyle }) {
if (node.type === 'TEXT') {
const lastFill = getPaint(node.fills);
if (lastFill) {
middleStyle.color = colorString(lastFill.color);
}

const lastStroke = getPaint(node.strokes);
if (lastStroke) {
const weight = node.strokeWeight || 1;
middleStyle.WebkitTextStroke = `${weight}px ${colorString(lastStroke.color)}`;
}

const fontStyle = node.style;
applyFontStyle(middleStyle, fontStyle);

middleStyle.display = 'flex';
middleStyle.maxWidth = '-webkit-fill-available';
middleStyle.alignContent = 'flex-start';

if (fontStyle.textAlignHorizontal === 'CENTER') {
middleStyle.justifyContent = 'center';
middleStyle.textAlign = 'center';
} else if (fontStyle.textAlignHorizontal === 'LEFT') {
middleStyle.justifyContent = 'flex-start';
middleStyle.textAlign = 'left';
} else if (fontStyle.textAlignHorizontal === 'RIGHT') {
middleStyle.justifyContent = 'flex-end';
middleStyle.textAlign = 'right';
}

if (fontStyle.textAlignVertical === 'CENTER') {
middleStyle.verticalAlign = 'middle';
middleStyle.alignItems = 'center';
middleStyle.alignContent = 'center';
} else if (fontStyle.textAlignVertical === 'TOP') {
middleStyle.verticalAlign = 'top';
middleStyle.alignItems = 'flex-start';
middleStyle.alignContent = 'flex-start';
} else if (fontStyle.textAlignVertical === 'BOTTOM') {
middleStyle.verticalAlign = 'bottom';
middleStyle.alignItems = 'flex-end';
middleStyle.alignContent = 'flex-end';
}

const addValue = (name, value) => {
middleStyle[name] = `${middleStyle[name] ? `${middleStyle[name]}, ` : ''}${value}`;
};

if (node.effects) {
for (let i = 0; i < node.effects.length; i++) {
const effect = node.effects[i];

if (effect.visible === false) {
continue;
}

if (effect.type === 'DROP_SHADOW') {
if (Object.keys(props).includes('filterShadow')) {
addValue('filter', `drop-shadow(${dropShadow(effect)})`);
} else {
addValue('textShadow', dropShadow(effect));
}
}
if (effect.type === 'INNER_SHADOW') {
if (Object.keys(props).includes('filterShadow')) {
addValue('filter', `drop-shadow(${innerShadow(effect)})`);
} else {
addValue('textShadow', innerShadow(effect));
}
}
}
}

if (Object.keys(props).includes('input')) {
const inputId = printStyle({
flex: 1,
height: '100%'
});
content.push(`<input`);
if (!Object.keys(nodeProps).includes('id')) content.push(`id='${node.id}'`);
Object.keys(nodeProps).forEach(key => {
content.push(`${key}={${nodeProps[key]}}`);
});
content.push(`className='${inputId}${classNames.length ? ' ' : ''}${classNames.join(' ')}'`);
content.push(
`
type="${props.input || 'text'}"
placeholder="${node.characters}" />`
);

clearObject(nodeProps);
} else {
let para = '';
const styleCache = {};
let currStyle = 0;
let currStyleIndex = 0;

const maxCurrStyle = Object.keys(node.styleOverrideTable)
.map(s => Number.parseInt(s, 10))
.reduce((key, max) => (key > max ? key : max), 0);

const commitParagraph = key => {
if (styleCache[currStyle] == null) {
styleCache[currStyle] = {};
}

if (node.styleOverrideTable[currStyle] && node.styleOverrideTable[currStyle].fills) {
const lastFill = getPaint(node.styleOverrideTable[currStyle].fills);
if (lastFill) {
if (lastFill.type === 'SOLID') {
styleCache[currStyle].color = colorString(lastFill.color);
middleStyle.opacity = lastFill.opacity;
}
}
}

applyFontStyle(styleCache[currStyle], node.styleOverrideTable[currStyle]);

if (
(Object.keys(props).includes('ellipsis') && props.ellipsis == null) ||
(props.ellipsis &&
props.ellipsis
.split(',')
.map(s => Number.parseInt(s, 10))
.includes(currStyleIndex))
) {
styleCache[currStyle].overflow = 'hidden';
styleCache[currStyle].textOverflow = 'ellipsis';
}

if (Object.keys(props).includes('ellipsisFlex') && maxCurrStyle === currStyle) {
styleCache[currStyle].flex = 1;
}

if (Object.keys(props).includes('ellipsisWrap') && maxCurrStyle === currStyle) {
middleStyle.flexWrap = 'wrap';
}

const id = printStyle(styleCache[currStyle]);

para = para.replace(/\"/g, '\\"');
const spaceBefore = Array(para.length - para.trimLeft().length)
.fill('&nbsp;')
.join('');
const spaceAfter = Array(para.length - para.trimRight().length)
.fill('&nbsp;')
.join('');
para = para.trim();

if (id) content.push(`<span className="${id}" key="${key}">${spaceBefore}{\`${para}\`}${spaceAfter}</span>`);
else content.push(`<span key="${key}">${spaceBefore}{\`${para}\`}${spaceAfter}</span>`);

para = '';
currStyleIndex++;
};

for (const i in node.characters) {
let idx = node.characterStyleOverrides && node.characterStyleOverrides[i];
const char = node.characters[i];

if (node.characters[i] === '\n' && node.characters[i - 1] === '\n') {
const id = printStyle({
flex: 1,
minWidth: '-webkit-fill-available'
});
content.push(`<div className="${id}" key="${`br${i}`}">&nbsp;</div>`);
} else if (node.characters[i] === '\n') {
commitParagraph(i);

const id = printStyle({
flex: 1,
content: '""',
minWidth: '-webkit-fill-available'
});
content.push(`<br className="${id}" key="${`br${i}`}" />`);
middleStyle.flexWrap = 'wrap';
} else {
if (idx == null) {
idx = 0;
}

if (idx !== currStyle) {
commitParagraph(i);
currStyle = idx;
}

para += char;
}
}
commitParagraph('end');
}
}
}

module.exports = {
applyStyles,
Expand All @@ -169,5 +400,8 @@ module.exports = {
renderIfFalse,
setOnClick,
setId,
addClassName
addClassName,
addValue,
addOnChange,
setTextRenderer
};
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function getConfig(options = {}) {
process.exit(0);
}

Object.assign(options, presets[presetName]);
Object.assign({}, presets[presetName], options);

options.dir = options.dir || process.env.FIGMA_DIR;
options.makeDir = options.makeDir == null ? !!process.env.FIGMA_MAKE_DIR : options.makeDir;
Expand Down
19 changes: 14 additions & 5 deletions lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ module.exports = {
loadImageToDisk,
loadImageFromImagesToDisk,
loadImageFromRefImagesToDisk,
saveSvgToDisk
saveSvgToDisk,
clearObject
};

function typeFactoryDefault({ props }) {
return `{ ${Object.keys(props)
.map(key => `${key}: ${props[key] || 'any'};\n`)
.map(key => `${key}?: ${props[key] || 'any'};\n`)
.join('')} }`;
}

Expand Down Expand Up @@ -695,7 +696,7 @@ async function createComponent(component, parentShared) {
await visitNode(shared, component);

// Render props
const decorator = options.decorator || 'observer';
const decorator = options.decorator || '';
const typeFactory = options.typeFactory || typeFactoryDefault;
preprint(
`export const ${instance}: React.FC<${typeFactory(shared)}> = ${decorator}(props => { ${
Expand Down Expand Up @@ -752,9 +753,9 @@ async function generateComponentFile({ path, instance, fileName, name }, options
componentSrc += `import { ${instance} } from './${fileName}${options.fileAfterFix}';\n`;
componentSrc += `\n`;

const decorator = options.decorator || 'observer';
const decorator = options.decorator || '';

componentSrc += `export const ${name} = ${decorator}(props => {\n`;
componentSrc += `export const ${name}: typeof ${instance} = ${decorator}(props => {\n`;
componentSrc += `return <${instance} {...props} />;\n`;
componentSrc += `});\n`;
await writeFile(path, componentSrc, options);
Expand Down Expand Up @@ -844,3 +845,11 @@ async function loadImageFromRefImagesToDisk(imageRef, shared) {
const fileName = imageRef.replace(/\W+/g, '-');
return loadImageToDisk(refImages[imageRef], fileName, shared);
}

function clearObject(obj) {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
delete obj[key];
}
}
}
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "figma-react",
"description": "This is a tool to help you export Figma project into React mockups",
"version": "1.0.15",
"version": "1.0.18",
"private": false,
"author": "Maxim Karpov <[email protected]>",
"repository": {
Expand Down
Loading

0 comments on commit c065c8c

Please sign in to comment.