Skip to content

Commit 58de12e

Browse files
committed
Create component
1 parent 81ab9a8 commit 58de12e

File tree

3 files changed

+113
-7
lines changed

3 files changed

+113
-7
lines changed

src/Element.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import {
55
filterPropsByChildrenCountAndType,
66
fixChildrenText,
77
formatSvg,
8+
getElementProps,
89
organizeProps,
910
propsToComponentProps,
1011
propsToPropsWithTypography,
1112
space,
1213
} from './utils'
1314
import { extractKeyValueFromCssVar } from './utils/extract-key-value-from-css-var'
1415
import { textSegmentToTypography } from './utils/text-segment-to-typography'
16+
import { toCamel } from './utils/to-camel'
1517
import { toPascal } from './utils/to-pascal'
1618

1719
export type ComponentType =
@@ -403,8 +405,40 @@ export class Element {
403405
.join(' ')
404406
const body = `${space(dep)}<${componentType}${propsString ? ' ' + propsString : ''}${hasChildren ? '' : ' /'}>${hasChildren ? `\n${space(dep + 1)}${renderChildren}\n` : ''}${hasChildren ? `${space(dep)}</${componentType}>` : ''}`
405407

406-
if (this.node.type === 'INSTANCE' && this.componentType !== 'Image')
407-
return space(dep) + `<${toPascal(this.node.name)} />`
408+
if (this.node.type === 'INSTANCE' && this.componentType !== 'Image') {
409+
const { componentProperties } = this.node
410+
let componentChildren = ''
411+
const props = Object.entries(componentProperties)
412+
.filter(([key, value]) => {
413+
if (
414+
value.type === 'TEXT' &&
415+
toCamel(key.split('#')[0]) === 'children'
416+
) {
417+
componentChildren += value.value
418+
return false
419+
}
420+
return true
421+
})
422+
.map(getElementProps)
423+
.join(' ')
424+
const content =
425+
space(dep) +
426+
`<${toPascal(this.node.name)}${props ? ' ' + props : ''}${
427+
!componentChildren
428+
? ' />'
429+
: `>\n${space(dep + 1)}${componentChildren}\n${space(dep)}</${toPascal(this.node.name)}>`
430+
}`
431+
432+
if (!this.parent) {
433+
const mainComponent = await this.node.getMainComponentAsync()
434+
if (mainComponent) {
435+
const mainComponentElement = new Element(mainComponent)
436+
const mainComponentChildren = await mainComponentElement.render(dep)
437+
return `${content}\n\n/*\n${mainComponentChildren}\n*/`
438+
}
439+
}
440+
return content
441+
}
408442

409443
if (this.node.type === 'COMPONENT') {
410444
const componentName = toPascal(this.node.name)

src/__tests__/Element.test.ts

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,20 @@ function createNode(
1515
layoutSizingHorizontal,
1616
styledTextSegments = [],
1717
variantProperties,
18+
componentProperties,
19+
getMainComponentAsync,
1820
...props
1921
}: {
2022
[_: string]: any
2123
characters?: string
2224
name?: string
25+
componentProperties?: ComponentProperties
2326
textStyleId?: string
2427
children?: SceneNode[]
2528
layoutPositioning?: string
2629
styledTextSegments?: any[]
2730
variantProperties?: Record<string, string>
31+
getMainComponentAsync?: () => Promise<ComponentNode | null>
2832
} = {},
2933
): SceneNode {
3034
const ret = {
@@ -44,6 +48,8 @@ function createNode(
4448
fills,
4549
variantProperties,
4650
children: children ?? [],
51+
componentProperties,
52+
getMainComponentAsync: getMainComponentAsync ?? (async () => null),
4753
} as unknown as SceneNode
4854
;(ret as any).children.forEach((child: any) => {
4955
;(child as any).parent = ret
@@ -1213,10 +1219,69 @@ describe('Element', () => {
12131219

12141220
describe('Instance', () => {
12151221
it('should render Instance', async () => {
1216-
const element = createElement('INSTANCE', {
1217-
name: 'Instance',
1218-
})
1219-
expect(await element.render()).toEqual('<Instance />')
1222+
{
1223+
const element = createElement('INSTANCE', {
1224+
name: 'Instance',
1225+
componentProperties: {},
1226+
})
1227+
expect(await element.render()).toEqual('<Instance />')
1228+
}
1229+
{
1230+
const element = createElement('INSTANCE', {
1231+
name: 'Instance',
1232+
componentProperties: {
1233+
children: { type: 'TEXT', value: 'Hello' },
1234+
},
1235+
})
1236+
expect(await element.render()).toEqual(
1237+
'<Instance>\n Hello\n</Instance>',
1238+
)
1239+
}
1240+
{
1241+
const element = createElement('INSTANCE', {
1242+
name: 'Instance',
1243+
componentProperties: {
1244+
children: { type: 'TEXT', value: 'Hello' },
1245+
color: { type: 'TEXT', value: 'red' },
1246+
width: { type: 'TEXT', value: '100px' },
1247+
height: { type: 'TEXT', value: '100px' },
1248+
danger: { type: 'BOOLEAN', value: true },
1249+
},
1250+
})
1251+
expect(await element.render()).toEqual(
1252+
'<Instance color="red" width="100px" height="100px" danger>\n Hello\n</Instance>',
1253+
)
1254+
}
1255+
{
1256+
const element = createElement('INSTANCE', {
1257+
name: 'Instance',
1258+
componentProperties: {
1259+
children: { type: 'TEXT', value: 'Hello' },
1260+
color: { type: 'TEXT', value: 'red' },
1261+
width: { type: 'TEXT', value: '100px' },
1262+
height: { type: 'TEXT', value: '100px' },
1263+
danger: { type: 'BOOLEAN', value: true },
1264+
},
1265+
getMainComponentAsync: async () =>
1266+
createNode('COMPONENT', {
1267+
name: 'MainComponent',
1268+
children: [],
1269+
}),
1270+
})
1271+
expect(await element.render()).toEqual(
1272+
`<Instance color="red" width="100px" height="100px" danger>
1273+
Hello
1274+
</Instance>
1275+
1276+
/*
1277+
export function MainComponent() {
1278+
return (
1279+
<Box />
1280+
)
1281+
}
1282+
*/`,
1283+
)
1284+
}
12201285
})
12211286
})
12221287

src/utils.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,14 @@ export function createInterface(
652652
if (!props || Object.keys(props).length === 0) return null
653653
return `export interface ${componentName}Props {
654654
${Object.keys(props)
655-
.map((key) => ` ${key}: unknown`)
655+
.map((key) => ` ${toCamel(key)}: unknown`)
656656
.join('\n')}
657657
}`
658658
}
659+
660+
export function getElementProps(props: [string, ComponentProperties[string]]) {
661+
const [key, value] = props
662+
const propKey = toCamel(key.split('#')[0])
663+
if (value.type === 'BOOLEAN' && value.value) return propKey
664+
return `${propKey}="${value.value}"`
665+
}

0 commit comments

Comments
 (0)