Skip to content

Commit 1fcd313

Browse files
committed
Fix many style text issue
1 parent ba9c63a commit 1fcd313

File tree

4 files changed

+121
-12
lines changed

4 files changed

+121
-12
lines changed

src/Element.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import {
99
space,
1010
} from './utils'
1111
import { extractKeyValueFromCssVar } from './utils/extract-key-value-from-css-var'
12+
import { textSegmentToTypography } from './utils/text-segment-to-typography'
1213

1314
export type ComponentType =
15+
| 'Fragment'
1416
| 'Box'
1517
| 'Text'
1618
| 'Button'
@@ -21,6 +23,21 @@ export type ComponentType =
2123
| 'Image'
2224
| 'Grid'
2325
| 'svg'
26+
const SEGMENT_TYPE = [
27+
'fontName',
28+
'fontWeight',
29+
'fontSize',
30+
'textDecoration',
31+
'textCase',
32+
'lineHeight',
33+
'letterSpacing',
34+
'fills',
35+
'textStyleId',
36+
'fillStyleId',
37+
'listOptions',
38+
'indentation',
39+
'hyperlink',
40+
] as (keyof Omit<StyledTextSegment, 'characters' | 'start' | 'end'>)[]
2441

2542
export class Element {
2643
node: SceneNode
@@ -216,6 +233,40 @@ export class Element {
216233
const originProps = await this.getProps()
217234
const mergedProps = { ...originProps, ...this.additionalProps }
218235
const children = this.getChildren()
236+
237+
if (this.node.type === 'TEXT') {
238+
const segs = this.node.getStyledTextSegments(SEGMENT_TYPE)
239+
if (segs.length > 1) {
240+
const children = (
241+
await Promise.all(
242+
segs.map(async (seg) => {
243+
const props = propsToComponentProps(
244+
organizeProps({
245+
...mergedProps,
246+
...Object.fromEntries(
247+
Object.entries(
248+
await propsToPropsWithTypography(
249+
(await textSegmentToTypography(seg)) as any,
250+
seg.textStyleId,
251+
),
252+
)
253+
.filter(([_, value]) => Boolean(value))
254+
.map(([key, value]) => [key, String(value)]),
255+
),
256+
}),
257+
'Text',
258+
1,
259+
)
260+
return `<Text ${Object.entries(props)
261+
.map(([key, value]) => `${key}="${value}"`)
262+
.join(' ')}>${fixChildrenText(seg.characters)}</Text>`
263+
}),
264+
)
265+
).join('')
266+
return `<>${children}</>`
267+
}
268+
}
269+
219270
const props = organizeProps(
220271
this.node.type === 'TEXT'
221272
? await propsToPropsWithTypography(mergedProps, this.node.textStyleId)

src/__tests__/Element.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ function createNode(
1313
parent,
1414
layoutPositioning = 'AUTO',
1515
layoutSizingHorizontal,
16+
styledTextSegments = [],
1617
...props
1718
}: {
1819
[_: string]: any
@@ -21,12 +22,14 @@ function createNode(
2122
textStyleId?: string
2223
children?: SceneNode[]
2324
layoutPositioning?: string
25+
styledTextSegments?: any[]
2426
} = {},
2527
): SceneNode {
2628
const ret = {
2729
type,
2830
getCSSAsync: async () => props,
2931
exportAsync: async () => '<svg>\n<path/>\n</svg>',
32+
getStyledTextSegments: () => styledTextSegments,
3033
layoutSizingHorizontal,
3134
textStyleId,
3235
parent,
@@ -577,6 +580,59 @@ describe('Element', () => {
577580
'<Text typography="button-title">\n a\n</Text>',
578581
)
579582
})
583+
it('should render many Text', async () => {
584+
const element = createElement('TEXT', {
585+
characters: 'ab',
586+
styledTextSegments: [
587+
{
588+
start: 0,
589+
end: 1,
590+
fontName: {
591+
family: 'Roboto',
592+
style: 'Italic',
593+
},
594+
textDecoration: 'NONE',
595+
fontWeight: 400,
596+
textCase: 'UPPER',
597+
lineHeight: {
598+
value: 20,
599+
unit: 'PIXELS',
600+
},
601+
letterSpacing: {
602+
value: 20,
603+
unit: 'PIXELS',
604+
},
605+
characters: 'a',
606+
fontSize: 16,
607+
},
608+
{
609+
fontSize: 16,
610+
characters: 'b',
611+
start: 1,
612+
end: 2,
613+
fontName: {
614+
family: 'Roboto',
615+
style: 'Italic',
616+
},
617+
textCase: 'UPPER',
618+
textDecoration: 'NONE',
619+
fontWeight: 700,
620+
lineHeight: {
621+
value: 20,
622+
unit: 'PIXELS',
623+
},
624+
letterSpacing: {
625+
value: 20,
626+
unit: 'PIXELS',
627+
},
628+
},
629+
],
630+
})
631+
expect(await element.getComponentType()).toEqual('Text')
632+
expect(await element.render()).toEqual(
633+
'<><Text fontFamily="Roboto" fontStyle="italic" fontWeight="400" fontSize="16px" textTransform="upper" lineHeight="20px" letterSpacing="20px">a</Text><Text fontFamily="Roboto" fontStyle="italic" fontWeight="700" fontSize="16px" textTransform="upper" lineHeight="20px" letterSpacing="20px">b</Text></>',
634+
)
635+
})
580636
})
581637
describe('Flex', () => {
582638
it('should render Flex', async () => {

src/devup/index.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export async function exportDevup() {
6363
return
6464
}
6565
}
66-
const seg = text.getStyledTextSegments([
66+
const segs = text.getStyledTextSegments([
6767
'fontName',
6868
'fontWeight',
6969
'fontSize',
@@ -77,16 +77,18 @@ export async function exportDevup() {
7777
'listOptions',
7878
'indentation',
7979
'hyperlink',
80-
])[0]
81-
if (seg) {
82-
const typo = textSegmentToTypography(seg as StyledTextSegment)
83-
typography[name] ??= [null, null, null, null, null]
84-
if (type === 'mobile') {
85-
typography[name][0] = typo
86-
} else if (type === 'tablet') {
87-
typography[name][2] = typo
88-
} else if (type === 'desktop') {
89-
typography[name][4] = typo
80+
])
81+
for (const seg of segs) {
82+
if (seg) {
83+
const typo = textSegmentToTypography(seg as StyledTextSegment)
84+
typography[name] ??= [null, null, null, null, null]
85+
if (type === 'mobile') {
86+
typography[name][0] = typo
87+
} else if (type === 'tablet') {
88+
typography[name][2] = typo
89+
} else if (type === 'desktop') {
90+
typography[name][4] = typo
91+
}
9092
}
9193
}
9294
}

src/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export async function propsToPropsWithTypography(
9393
const ret: Record<string, string> = { ...props }
9494
delete ret['w']
9595
delete ret['h']
96-
if (typeof textStyleId === 'string') {
96+
if (typeof textStyleId === 'string' && textStyleId) {
9797
const style = await figma.getStyleByIdAsync(textStyleId as string)
9898
if (style) {
9999
const split = style.name.split('/')

0 commit comments

Comments
 (0)