diff --git a/src/sync/reader/figma/parser.ts b/src/sync/reader/figma/parser.ts index 3448b277..6fd3cd21 100644 --- a/src/sync/reader/figma/parser.ts +++ b/src/sync/reader/figma/parser.ts @@ -1,6 +1,6 @@ -import { Node, Style, StylesMap } from 'figma-api'; +import { Node, Style, StylesMap, EffectType } from 'figma-api'; import { GetFileResult } from 'figma-api/lib/api-types'; -import Token, { TokenType } from '../../../token'; +import Token, { TokenType, TokenShadow } from '../../../token'; import TokenCollection from '../../../token-collection'; import FigmaReaderConfig from './config'; import Referencer from './referencers/referencer'; @@ -84,6 +84,7 @@ export default class FigmaParser { const token = this.createToken(this.getNameFromText(node)); token.value = this.getValueFromText(node); + token.category = 'content'; this.tokens.add(token); } @@ -115,6 +116,8 @@ export default class FigmaParser { // anyway look for the value else { const key = `${type.toLowerCase()}s` as keyof Node<'VECTOR'>; + + // fill - color swatch if (key === 'fills' && node[key]) { // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore @@ -123,6 +126,38 @@ export default class FigmaParser { visible: node[key][0].visible ?? true }; } + + // effect - shadows + else if (key === 'effects' && node[key]) { + const shadows: TokenShadow[] = []; + + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore + for (const effect of node[key]) { + if (!effect.visible) { + return; + } + + if ( + effect.type === EffectType.DROP_SHADOW || + effect.type === EffectType.INNER_SHADOW + ) { + shadows.push({ + inner: effect.type === EffectType.INNER_SHADOW, + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore + color: { ...effect.color, visible: true }, + x: effect.offset?.x ?? 0, + y: effect.offset?.y ?? 0, + radius: effect.radius + }); + } + } + + if (shadows.length > 0) { + token.shadows = shadows; + } + } } this.tokens.add(token); } @@ -164,6 +199,9 @@ export default class FigmaParser { case 'stroke': return 'color'; + case 'effects': + return 'shadow'; + // case 'TEXT': // return 'typography'; diff --git a/src/token.ts b/src/token.ts index f25a4e3e..784c197a 100644 --- a/src/token.ts +++ b/src/token.ts @@ -38,6 +38,22 @@ export enum TokenType { Component = 'component' } +export interface TokenColor { + r: number; + g: number; + b: number; + a: number; + visible: boolean; +} + +export interface TokenShadow { + inner: boolean; + x: number; + y: number; + radius: number; + color: TokenColor; +} + /** * DTO to describe a Design Token * @@ -134,11 +150,10 @@ export default interface Token { * * - `visible` - if completely transparent */ - color?: { - r: number; - g: number; - b: number; - a: number; - visible: boolean; - }; + color?: TokenColor; + + /** + * Shadows values + */ + shadows?: TokenShadow[]; } diff --git a/src/tools/style-dictionary/writer.ts b/src/tools/style-dictionary/writer.ts index fe9d03c9..9e760797 100644 --- a/src/tools/style-dictionary/writer.ts +++ b/src/tools/style-dictionary/writer.ts @@ -7,7 +7,7 @@ import WriterConfig, { ColorFormat, ColorAlphaFormat } from '../../sync/writer/config'; -import Token from '../../token'; +import Token, { TokenColor } from '../../token'; import TokenCollection from '../../token-collection'; import { set } from '../../utils'; @@ -69,39 +69,62 @@ export default class StyleDictionaryWriter { private getValue(token: Token): string { if (token.color) { - const c = token.color; + return this.getColor(token.color); + } - if (c.visible === false) { - return 'transparent'; + if (token.shadows) { + const shadows = []; + for (const shadow of token.shadows) { + shadows.push( + `${shadow.inner ? 'inset ' : ''}${shadow.x} ${shadow.y} ${ + shadow.radius + } ${this.getColor(shadow.color)}` + ); } - const color = cc.fromRGBA(c.r * 255, c.g * 255, c.b * 255, c.a); + return shadows.join(', '); + } - if (color.alpha === 1) { - switch (this.config.formats.color) { - case ColorFormat.Rgb: - return color.toRGB(); + return token.value ?? ''; + } - case ColorFormat.Hsl: - return color.toHSL(); + private getColor(color: TokenColor): string { + if (color.visible === false) { + return 'transparent'; + } + + const c = cc.fromRGBA(color.r * 255, color.g * 255, color.b * 255, color.a); - case ColorFormat.Hex: - default: - return color.toHex(); + if (c.alpha === 1) { + switch (this.config.formats.color) { + case ColorFormat.Rgb: { + const { r, g, b } = c.toRGB(); + return `rgb(${r}, ${g}, ${b})`; } - } - switch (this.config.formats.colorAlpha) { - case ColorAlphaFormat.Hsl: - return color.toHSLA(); + case ColorFormat.Hsl: { + const { h, s, l } = c.toHSL(); + return `hsl(${h}, ${s}, ${l})`; + } - case ColorAlphaFormat.Rgb: + case ColorFormat.Hex: default: - return color.toRGBA(); + return c.toHex(); } } - return token.value ?? ''; + switch (this.config.formats.colorAlpha) { + case ColorAlphaFormat.Hsl: { + const { h, s, l, a } = c.toHSLA(); + return `hsla(${h}, ${s}, ${l}, ${a})`; + } + + case ColorAlphaFormat.Rgb: + default: { + const { r, g, b, a } = c.toRGBA(); + return `rgba(${r}, ${g}, ${b}, ${a})`; + } + } } private writeFile(file: string, data: object) {