diff --git a/src/types/css.ts b/src/types/css.ts deleted file mode 100644 index 2b987f5..0000000 --- a/src/types/css.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { gql } from "apollo-server-express"; - -export const type = gql` - type CSS { - # CSS compatible textAlign property - textAlign: String - # CSS compatible veticalAlign property - verticalAlign: String - # CSS compatible lineHeight - lineHeight: String - } -`; - -export const resolvers = { - CSS: { - textAlign: root => root.textAlignHorizontal.toLowerCase(), - verticalAlign: root => root.textAlignVertical.toLowerCase(), - lineHeight: root => `${root.lineHeightPx}px`, - }, -}; diff --git a/src/types/helpers/style.ts b/src/types/helpers/style.ts index 66b7741..5f63b63 100644 --- a/src/types/helpers/style.ts +++ b/src/types/helpers/style.ts @@ -128,6 +128,18 @@ export const type = gql` # gradient stop position position: Float } + + enum ColorMode { + RGB + HEX + HSL + } `; +export enum ColorMode { + RGB, + HEX, + HSL, +} + export const resolvers = {}; diff --git a/src/types/node.ts b/src/types/node.ts index f92b0e7..2605a2f 100644 --- a/src/types/node.ts +++ b/src/types/node.ts @@ -3,6 +3,7 @@ import { createBatchResolver } from "graphql-resolve-batch"; import camelCase from "lodash/camelCase"; import groupBy from "lodash/groupBy"; import { loadImages } from "../utils/figma"; +import { generateCSS } from "../utils/helpers"; export const nodeProperties = ` # A string uniquely identifying this node within the document. @@ -18,7 +19,7 @@ export const nodeProperties = ` type: NodeType! # Additional properties - image(params: ImageNodeParams): Image + export(params: ExportParams): String `; export const nodeTypes = [ @@ -60,21 +61,31 @@ export const resolvers = { return capitalise(camelCase(nodeType)); }, - image: createBatchResolver(async (sources, { params }) => { + export: createBatchResolver(async (sources, { params }) => { const sourcesByFile = groupBy(sources, "fileId"); - const parsedImages = await Promise.all( - Object.entries(sourcesByFile).map(([fileId, nodes]) => - loadImages(fileId, { ...params, ids: nodes.map(({ id }) => id) }).then( - ({ images }) => images - ) - ) - ); - return parsedImages.reduce( - (acc: any[], parsedImage) => [ - ...acc, - ...Object.entries(parsedImage).map(entry => ({ id: entry[0], file: entry[1] })), - ], + const fileExports = + params && params.format === "css" + ? Object.entries(sourcesByFile).map(([fileId, nodes]) => + nodes.reduce( + (acc, node) => ({ + ...acc, + [node.id]: generateCSS(node), + }), + {} + ) + ) + : await Promise.all( + Object.entries(sourcesByFile).map(([fileId, nodes]) => + loadImages(fileId, { + ...params, + ids: nodes.map(({ id }) => id), + }).then(({ images }) => images) + ) + ); + + return fileExports.reduce( + (acc: any[], parsedImage) => [...acc, ...Object.values(parsedImage)], [] ); }), diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 6a31a6e..2ea6a57 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -1,3 +1,6 @@ +import { Color } from "figma-js"; +import { ColorMode } from "../types/helpers/style"; + export const getPosition = node => ({ x: node.absoluteBoundingBox.x, y: node.absoluteBoundingBox.y, @@ -7,3 +10,34 @@ export const getSize = node => ({ width: node.absoluteBoundingBox.width, height: node.absoluteBoundingBox.height, }); + +export const addUnit = (string?: string | number, unit: string = "px") => + string != null ? `${string}${unit}` : string; + +export const getColor = ({ r, g, b, a }: Color, mode: ColorMode) => `rgba(${r}, ${g}, ${b}, ${a})`; + +export const JSToCSS = cssObject => { + return Object.entries(cssObject) + .map(([key, value]) => { + if (value == null) { + return value; + } + + return `${key.replace(/([A-Z])/g, g => `-${g[0].toLowerCase()}`)}: ${value};`; + }) + .filter(Boolean) + .join(" "); +}; + +export const generateCSS = node => { + const styles = { + textAlign: node.style && node.style.textAlignHorizontal.toLowerCase(), + verticalAlign: node.style && node.style.textAlignVertical.toLowerCase(), + lineHeight: node.style && addUnit(node.style.lineHeightPx), + width: node.absoluteBoundingBox && addUnit(node.absoluteBoundingBox.width), + height: node.absoluteBoundingBox && addUnit(node.absoluteBoundingBox.height), + backgroundColor: node.backgroundColor && getColor(node.backgroundColor, ColorMode.RGB), + }; + + return JSToCSS(styles); +};