├── src ├── app │ ├── declarations.d.ts │ ├── components │ │ ├── Style │ │ │ ├── AlignmentSection.tsx │ │ │ ├── Style.tsx │ │ │ └── ColorSection.tsx │ │ ├── Info │ │ │ └── Info.tsx │ │ ├── Layout │ │ │ ├── Footer.tsx │ │ │ ├── Layout.tsx │ │ │ └── Header.tsx │ │ ├── App.tsx │ │ ├── About │ │ │ └── About.tsx │ │ ├── routes.tsx │ │ ├── Axes │ │ │ ├── Axes.tsx │ │ │ └── Axis.tsx │ │ ├── Token │ │ │ └── Token.tsx │ │ ├── Font │ │ │ ├── Font.tsx │ │ │ └── FontUpload.tsx │ │ ├── Glyphs │ │ │ └── Glyphs.tsx │ │ ├── Preview │ │ │ └── Preview.tsx │ │ └── Instances │ │ │ └── Instances.tsx │ ├── store │ │ ├── configStore.ts │ │ ├── rootReducer.ts │ │ └── activeTextSlice.ts │ ├── hooks │ │ ├── useGetToken.ts │ │ ├── useGetHbInstance.ts │ │ ├── useFetchFigmaMessages.ts │ │ └── useGetFontList.ts │ ├── utils │ │ ├── fontUtils.ts │ │ ├── updateOnFigma.ts │ │ ├── convertHtmlToText.ts │ │ ├── getFontData.ts │ │ ├── googleVariableFontList.ts │ │ └── convertTextToSvg.ts │ ├── index.html │ ├── consts.ts │ ├── index.tsx │ ├── common │ │ ├── Section.tsx │ │ ├── Global.styles.ts │ │ ├── Spinner.tsx │ │ ├── RadioButtons.tsx │ │ ├── FileUploadDropzone.tsx │ │ └── CustomSelect.tsx │ ├── types.ts │ └── context │ │ └── stateContext.tsx ├── libraries │ ├── hb.wasm │ └── hbjs.js └── plugin │ ├── init.ts │ ├── controller.ts │ ├── constants.ts │ ├── utils.ts │ ├── glyphs.ts │ └── events.ts ├── .gitignore ├── screenshot.png ├── variable-fonts-lambda ├── .gitignore ├── configs │ ├── local.yml │ ├── dev.yml │ └── prod.yml ├── .travis.yml ├── modules │ ├── redirects.js │ └── fonts │ │ └── endpoints │ │ ├── callback.js │ │ ├── token.js │ │ ├── delete.js │ │ ├── update.js │ │ ├── read.js │ │ └── upload.js ├── Dockerfile ├── .jshintrc ├── handlers │ ├── _not-found.yml │ └── fonts-endpoints.yml ├── shared │ └── lib │ │ ├── uuid.js │ │ ├── response.js │ │ ├── parsers.js │ │ ├── kinesis.js │ │ ├── lambda.js │ │ └── dynamo.js ├── serverless-dynamic.js ├── docker-compose.yml ├── LICENSE ├── package.json ├── serverless.yml ├── deploy-policy.json └── README.md ├── .prettierrc.yml ├── manifest.json ├── tsconfig.json ├── package.json ├── webpack.config.js ├── README.md └── LICENSE.txt /src/app/declarations.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.ttf'; 2 | declare module '*.wasm'; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | settings.json 4 | yarn-error.log 5 | *.DS_Store 6 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tgemayel/variable-fonts-figma/HEAD/screenshot.png -------------------------------------------------------------------------------- /variable-fonts-lambda/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | .serverless 4 | .tmp 5 | .terraform -------------------------------------------------------------------------------- /src/libraries/hb.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tgemayel/variable-fonts-figma/HEAD/src/libraries/hb.wasm -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | trailingComma: es5 2 | singleQuote: true 3 | printWidth: 120 4 | tabWidth: 4 5 | bracketSpacing: true 6 | -------------------------------------------------------------------------------- /src/plugin/init.ts: -------------------------------------------------------------------------------- 1 | export const figmaInit = () => { 2 | figma.showUI(__html__, { 3 | width: 330, 4 | height: 600, 5 | }); 6 | }; 7 | -------------------------------------------------------------------------------- /src/plugin/controller.ts: -------------------------------------------------------------------------------- 1 | import { setupFigmaEvents } from './events'; 2 | import { figmaInit } from './init'; 3 | 4 | setupFigmaEvents(); 5 | figmaInit(); 6 | -------------------------------------------------------------------------------- /variable-fonts-lambda/configs/local.yml: -------------------------------------------------------------------------------- 1 | ENV: local 2 | MESSAGE: 'Local' 3 | DYNAMO_TABLE_FONTS: ${self:custom.dynamo-fonts} # Reference to Custom Env 4 | REGION: 'localhost' -------------------------------------------------------------------------------- /variable-fonts-lambda/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: node_js 3 | node_js: 4 | - "8" 5 | 6 | before_script: 7 | - npm install 8 | 9 | script: npm run test 10 | -------------------------------------------------------------------------------- /variable-fonts-lambda/configs/dev.yml: -------------------------------------------------------------------------------- 1 | ENV: dev 2 | MESSAGE: 'Hey Hey' 3 | DYNAMO_TABLE_FONTS: ${self:custom.dynamo-fonts} # Reference to Custom Env 4 | REGION: ${self:custom.region} -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Variable Fonts", 3 | "id": "00000000", 4 | "api": "1.0.0", 5 | "main": "dist/code.js", 6 | "ui": "dist/ui.html", 7 | "build": "yarn" 8 | } 9 | -------------------------------------------------------------------------------- /src/app/components/Style/AlignmentSection.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const AlignmentSection = () => { 4 | return <>Alignment; 5 | }; 6 | 7 | export default AlignmentSection; 8 | -------------------------------------------------------------------------------- /variable-fonts-lambda/configs/prod.yml: -------------------------------------------------------------------------------- 1 | ENV: prod 2 | MESSAGE: 'loko é poko' 3 | DYNAMO_TABLE_FONTS: ${self:custom.dynamo-fonts} # Reference to Custom Env 4 | S3_BUCKET: ${self:custom.font-bucket} 5 | REGION: ${self:custom.region} -------------------------------------------------------------------------------- /variable-fonts-lambda/modules/redirects.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const response = require('../shared/lib/response'); 4 | 5 | module.exports.notFound = (event, context, callback) => { 6 | return response.json(callback, null, 404); 7 | }; 8 | -------------------------------------------------------------------------------- /variable-fonts-lambda/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.19.0-alpine3.10 2 | 3 | RUN npm install -g serverless 4 | 5 | WORKDIR /app/ 6 | 7 | COPY ./package.json /app/package.json 8 | 9 | RUN npm install 10 | 11 | COPY . /app/ 12 | 13 | EXPOSE 3000 14 | 15 | CMD ["npm", "run", "dev"] -------------------------------------------------------------------------------- /variable-fonts-lambda/modules/fonts/endpoints/callback.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports.index = async (event) => { 4 | const { queryStringParameters: qs } = event; 5 | console.log(qs, event); 6 | return { 7 | statusCode: 201, 8 | body: qs.code 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /src/app/store/configStore.ts: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit'; 2 | import rootReducer from './rootReducer'; 3 | 4 | const store = configureStore({ 5 | reducer: rootReducer, 6 | }); 7 | 8 | export type AppDispatch = typeof store.dispatch; 9 | 10 | export default store; 11 | -------------------------------------------------------------------------------- /variable-fonts-lambda/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esversion": 6, 3 | "node": true, 4 | "camelcase": true, 5 | "strict": true, 6 | "globals": { 7 | "module": true, 8 | "exports": true, 9 | "require": false, 10 | "it": true, 11 | "describe": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /variable-fonts-lambda/handlers/_not-found.yml: -------------------------------------------------------------------------------- 1 | # Config for Not Found Endpoints 2 | notFound: 3 | handler: modules/redirects.notFound 4 | timeout: 30 5 | events: 6 | - http: 7 | path: /{proxy+} # catch any path not specified elsewhere 8 | method: any 9 | cors: true 10 | -------------------------------------------------------------------------------- /src/app/store/rootReducer.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from '@reduxjs/toolkit'; 2 | 3 | import activeTextReducer from './activeTextSlice'; 4 | 5 | const rootReducer = combineReducers({ 6 | activeText: activeTextReducer, 7 | }); 8 | 9 | export type RootState = ReturnType; 10 | 11 | export default rootReducer; 12 | -------------------------------------------------------------------------------- /variable-fonts-lambda/shared/lib/uuid.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function s4() { 4 | return Math.floor((1 + Math.random()) * 0x10000) 5 | .toString(16) 6 | .substring(1); 7 | } 8 | 9 | function uuid() { 10 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 11 | s4() + '-' + s4() + s4() + s4(); 12 | } 13 | 14 | module.exports = uuid; -------------------------------------------------------------------------------- /src/app/hooks/useGetToken.ts: -------------------------------------------------------------------------------- 1 | import { FIGMA_EVENT_TYPES } from '../../plugin/constants'; 2 | 3 | const useGetToken = () => { 4 | window.parent.postMessage( 5 | { 6 | pluginMessage: { 7 | type: FIGMA_EVENT_TYPES.GET_TOKEN, 8 | }, 9 | }, 10 | '*' 11 | ); 12 | }; 13 | 14 | export default useGetToken; 15 | -------------------------------------------------------------------------------- /variable-fonts-lambda/modules/fonts/endpoints/token.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { v4: uuidv4 } = require('uuid'); 4 | 5 | module.exports.index = async (event) => { 6 | return { 7 | statusCode: 201, 8 | headers: { 9 | 'Access-Control-Allow-Origin': '*', 10 | 'Access-Control-Allow-Credentials': true, 11 | }, 12 | body: uuidv4() 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /variable-fonts-lambda/shared/lib/response.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports.json = (callback, body = {}, status = 200) => { 4 | return callback(null, { 5 | statusCode: status, 6 | headers: { 7 | "Access-Control-Allow-Origin" : "*" 8 | }, 9 | body: (body != null) ? JSON.stringify(body.stack ? body.stack : body) : "" 10 | }); 11 | }; 12 | -------------------------------------------------------------------------------- /src/app/utils/fontUtils.ts: -------------------------------------------------------------------------------- 1 | export const defineFontFace = (name, url) => { 2 | const newStyle = document.createElement('style'); 3 | newStyle.appendChild( 4 | document.createTextNode( 5 | `@font-face { 6 | font-family: '${name}'; 7 | src: url('${url}'); 8 | }` 9 | ) 10 | ); 11 | 12 | document.head.appendChild(newStyle); 13 | }; 14 | -------------------------------------------------------------------------------- /src/app/index.html: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | -------------------------------------------------------------------------------- /src/app/components/Info/Info.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Text } from 'react-figma-plugin-ds'; 3 | import Section from '../../common/Section'; 4 | 5 | const Info = () => { 6 | return ( 7 |
8 | 9 | Family name: Roboto Extremo 10 | 11 |
12 | ); 13 | }; 14 | 15 | export default Info; 16 | -------------------------------------------------------------------------------- /variable-fonts-lambda/serverless-dynamic.js: -------------------------------------------------------------------------------- 1 | module.exports = () => { 2 | const fs = require('fs') 3 | const files = fs.readdirSync('./handlers') 4 | const YAML = require('yamljs') 5 | 6 | const merged = files 7 | .map(f => fs.readFileSync(`./handlers/${f}`, 'utf8')) 8 | .map(raw => YAML.parse(raw)) 9 | .reduce( (result, handler) => Object.assign(result, handler), {}) 10 | 11 | return merged 12 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "outDir": "dist", 5 | "jsx": "react", 6 | "noUnusedLocals": true, 7 | "noUnusedParameters": true, 8 | "experimentalDecorators": true, 9 | "removeComments": true, 10 | "noImplicitAny": false, 11 | "moduleResolution": "node", 12 | "typeRoots": [ 13 | "./node_modules/@types", 14 | "./node_modules/@figma" 15 | ], 16 | "resolveJsonModule": true 17 | } 18 | } -------------------------------------------------------------------------------- /src/app/consts.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_CONTENT = 'Default Text'; 2 | 3 | export const DEFAULT_COLOR = { 4 | r: 0, 5 | g: 0, 6 | b: 0, 7 | a: 1, 8 | }; 9 | 10 | export const GOOGLE_FONT_PATH = 'https://cdn.jsdelivr.net/gh/google/fonts@master/ofl'; 11 | 12 | export const CLIENT_ID = 's1qOlyGx7ygVSo55UEkvLt'; 13 | export const CLIENT_SECRET = 'FsZbhu8gUeKIV5y4gROmel0C6hPxZn'; 14 | export const CALLBACK = 'https://m99ul372hk.execute-api.us-east-1.amazonaws.com/prod/services/callback'; 15 | -------------------------------------------------------------------------------- /src/app/components/Layout/Footer.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from 'styled-components'; 3 | import { Text } from 'react-figma-plugin-ds'; 4 | 5 | const Footer = () => { 6 | return ( 7 | 8 | 9 | @ 2021 Variable Fonts {process.env.REACT_APP_VERSION} 10 | 11 | 12 | ); 13 | }; 14 | 15 | const Wrapper = styled.div` 16 | border-top: 1px solid #e5e5e5; 17 | padding: 0.625rem; 18 | `; 19 | 20 | export default Footer; 21 | -------------------------------------------------------------------------------- /src/app/hooks/useGetHbInstance.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import hbWasm from '../../libraries/hb.wasm'; 3 | import hbjs from '../../libraries/hbjs.js'; 4 | import { useAppState } from '../context/stateContext'; 5 | 6 | const useGetHbInstance = () => { 7 | const { setHbInstance } = useAppState(); 8 | 9 | React.useEffect(() => { 10 | hbWasm({}).then(({ instance }) => { 11 | instance.exports.memory.grow(400); 12 | setHbInstance(hbjs(instance)); 13 | }); 14 | }, []); 15 | }; 16 | 17 | export default useGetHbInstance; 18 | -------------------------------------------------------------------------------- /src/app/components/Style/Style.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | import Section from '../../common/Section'; 4 | import { RootState } from '../../store/rootReducer'; 5 | import ColorSection from './colorSection'; 6 | 7 | const Style = () => { 8 | const fontName = useSelector((state: RootState) => state.activeText.fontName); 9 | 10 | if (!fontName) return <>; 11 | 12 | return ( 13 |
14 | 15 |
16 | ); 17 | }; 18 | 19 | export default Style; 20 | -------------------------------------------------------------------------------- /variable-fonts-lambda/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | sqs: 4 | image: s12v/elasticmq:latest 5 | ports: 6 | - "9324:9324" 7 | networks: 8 | - developer 9 | 10 | dynamo: 11 | image: amazon/dynamodb-local:latest 12 | ports: 13 | - "8000:8000" 14 | networks: 15 | - developer 16 | 17 | serverless: 18 | build: . 19 | volumes: 20 | - "./:/app" 21 | ports: 22 | - "3000:3000" 23 | depends_on: 24 | - sqs 25 | - dynamo 26 | networks: 27 | - developer 28 | 29 | networks: 30 | developer: -------------------------------------------------------------------------------- /variable-fonts-lambda/shared/lib/parsers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports.parseObjectToString = obj => { 4 | if (typeof obj == "object") { 5 | return JSON.stringify(obj); 6 | } else { 7 | return obj; 8 | } 9 | } 10 | 11 | module.exports.parseStringToObject = str => { 12 | try { 13 | return JSON.parse(str); 14 | } catch (error) { 15 | return {}; 16 | } 17 | }; 18 | 19 | module.exports.parseEvent = event => { 20 | try { 21 | const body = event.body ? event.body : event; 22 | return JSON.parse(body); 23 | } catch (error) { 24 | return event; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /variable-fonts-lambda/modules/fonts/endpoints/delete.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const dynamo = require('../../../shared/lib/dynamo'); 4 | const response = require('../../../shared/lib/response'); 5 | 6 | const DYNAMO_TABLE_FONTS = process.env.DYNAMO_TABLE_FONTS || 'fonts'; 7 | 8 | module.exports.delete = (event, context, callback) => { 9 | 10 | const key = { hashkey: event.pathParameters.hashkey }; 11 | 12 | dynamo.removeRow(key, DYNAMO_TABLE_FONTS) 13 | .then(success => { 14 | 15 | response.json(callback, success, 204); 16 | 17 | }).catch(err => { 18 | 19 | response.json(callback, err, 500); 20 | 21 | }); 22 | 23 | }; -------------------------------------------------------------------------------- /src/app/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import { BrowserRouter } from 'react-router-dom'; 5 | import App from './components/App'; 6 | import store from './store/configStore'; 7 | 8 | import 'react-figma-plugin-ds/figma-plugin-ds.css'; 9 | import AppStateProvider from './context/stateContext'; 10 | 11 | ReactDOM.render( 12 | 13 | 14 | 15 | 16 | 17 | 18 | , 19 | document.getElementById('react-page') 20 | ); 21 | -------------------------------------------------------------------------------- /src/plugin/constants.ts: -------------------------------------------------------------------------------- 1 | export const NODE_TYPES = { 2 | TEXT: 'TEXT', 3 | }; 4 | 5 | export const FIGMA_EVENT_TYPES = { 6 | SELECTED_CHANGED: 'selected-change', 7 | ON_UI_LOADED: 'on-ui-loaded', 8 | RENDER_SVG: 'render-svg', 9 | UPDATE_SELECTED_VARIABLE_SETTINGS: 'update-selected-variable-settings', 10 | SET_TOKEN: 'set-token', 11 | GET_TOKEN: 'get-token', 12 | }; 13 | 14 | export const STATUSES = { 15 | SUCCESS: 'success', 16 | ERROR: 'error', 17 | }; 18 | 19 | export const NODE_PROPS = { 20 | IS_VARIABLE_FONT: 'is-variable-font', 21 | FONT_SIZE: 'node-font-size', 22 | FONT_COLOR: 'node-font-color', 23 | FONT_ALIGNMENT: 'node-font-alignment', 24 | FONT_NAME: 'node-font-name', 25 | TEXT_CONTENT: 'node-text-content', 26 | AXES: 'node-axes', 27 | }; 28 | -------------------------------------------------------------------------------- /src/app/common/Section.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Text } from 'react-figma-plugin-ds'; 3 | import styled from 'styled-components'; 4 | 5 | interface Props { 6 | label: string; 7 | children?: React.ReactNode | React.ReactNode[]; 8 | } 9 | 10 | const Section = ({ label, children }: Props) => { 11 | return ( 12 | 13 | 14 | 15 | {label} 16 | 17 | 18 | {children} 19 | 20 | ); 21 | }; 22 | 23 | const SectionWrapper = styled.div` 24 | padding: 0.625rem; 25 | `; 26 | 27 | const LabelWrapper = styled.div` 28 | margin-bottom: 0.5rem; 29 | `; 30 | 31 | export default Section; 32 | -------------------------------------------------------------------------------- /variable-fonts-lambda/shared/lib/kinesis.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const AWS = require('aws-sdk'); 4 | 5 | const kinesis = new AWS.Kinesis({ region: process.env.REGION || 'sa-east-1' }); 6 | const stream = process.env.KINESIS_STREAM || 'testStream'; 7 | 8 | /** 9 | * Kinesis Abstraction Library 10 | * @Author: Matheus 'Raj' Fidelis 11 | * @save() - Save Item on Kinesis 12 | */ 13 | const client = { 14 | 15 | save: (data, partition = '001') => { 16 | 17 | if (typeof data === 'object') { 18 | data = JSON.stringify(data); 19 | } 20 | 21 | const record = { 22 | Data: data, 23 | PartitionKey: partition, 24 | StreamName: stream 25 | }; 26 | 27 | return kinesis.putRecord(record).promise(); 28 | 29 | } 30 | 31 | }; 32 | 33 | module.exports = client; -------------------------------------------------------------------------------- /src/app/components/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { HashRouter } from 'react-router-dom'; 3 | import { route } from './routes'; 4 | 5 | import Spinner from '../common/Spinner'; 6 | import useFetchFigmaMessages from '../hooks/useFetchFigmaMessages'; 7 | import useGetFontList from '../hooks/useGetFontList'; 8 | import useGetHbInstance from '../hooks/useGetHbInstance'; 9 | import GlobalStyles from '../common/Global.styles'; 10 | import useGetToken from '../hooks/useGetToken'; 11 | 12 | const App = ({}) => { 13 | useGetToken(); 14 | useGetHbInstance(); 15 | useFetchFigmaMessages(); 16 | 17 | const { loading } = useGetFontList(); 18 | 19 | return ( 20 | <> 21 | 22 | {loading ? : } 23 | 24 | ); 25 | }; 26 | 27 | export default App; 28 | -------------------------------------------------------------------------------- /src/app/components/About/About.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from 'styled-components'; 3 | import { Text } from 'react-figma-plugin-ds'; 4 | import Section from '../../common/Section'; 5 | 6 | const About = () => { 7 | return ( 8 |
9 | 10 | 11 | Variable Fonts plugin is developed by Toni Gemayel. The 12 | variable fonts rendering engine is based on samsa.js by{' '} 13 | Laurence Penney. 14 | 15 | 16 |
17 | ); 18 | }; 19 | 20 | const Content = styled.div` 21 | padding-top: 1rem; 22 | padding-bottom: 1rem; 23 | `; 24 | 25 | export default About; 26 | -------------------------------------------------------------------------------- /src/app/common/Global.styles.ts: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components'; 2 | 3 | export default createGlobalStyle` 4 | html, body, div, span, applet, object, iframe, 5 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, 6 | abbr, acronym, address, big, cite, code, del, dfn, em, 7 | font, img, ins, kbd, q, s, samp, small, strike, strong, 8 | sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, 9 | fieldset, form, label, legend, table, caption, tbody, tfoot, 10 | thead, tr, th, td, input, textarea, keygen, select, button { 11 | margin: 0; 12 | padding: 0; 13 | outline: 0; 14 | border: 0; 15 | text-rendering: optimizeLegibility; 16 | -webkit-font-smoothing: antialiased; 17 | -webkit-text-size-adjust: 100%; 18 | } 19 | 20 | button { 21 | cursor: pointer; 22 | } 23 | 24 | .disclosure__content { 25 | user-select: all; 26 | pointer-events: all; 27 | } 28 | `; 29 | -------------------------------------------------------------------------------- /src/app/utils/updateOnFigma.ts: -------------------------------------------------------------------------------- 1 | import { FIGMA_EVENT_TYPES } from '../../plugin/constants'; 2 | import { convertTextToSvg } from './convertTextToSvg'; 3 | 4 | const asyncUpdateFigma = async (hbInstance, fontUrl, fontName, content, axes, color) => { 5 | const { paths } = await convertTextToSvg(hbInstance, fontUrl, content, { 6 | variations: axes, 7 | }); 8 | let codePoints = []; 9 | for (let i = 0; i < content.length; i++) { 10 | codePoints.push(content.charCodeAt(i)); 11 | } 12 | window.parent.postMessage( 13 | { 14 | pluginMessage: { 15 | type: FIGMA_EVENT_TYPES.UPDATE_SELECTED_VARIABLE_SETTINGS, 16 | payload: { 17 | paths, 18 | fontName: fontName, 19 | axes, 20 | codePoints, 21 | color, 22 | }, 23 | }, 24 | }, 25 | '*' 26 | ); 27 | }; 28 | 29 | export default asyncUpdateFigma; 30 | -------------------------------------------------------------------------------- /src/plugin/utils.ts: -------------------------------------------------------------------------------- 1 | export const addSpaces = (path: any) => { 2 | let newPath = path; 3 | newPath = newPath.split('M').join(' M '); 4 | newPath = newPath.split('m').join(' m '); 5 | newPath = newPath.split('L').join(' L '); 6 | newPath = newPath.split('l').join(' l '); 7 | newPath = newPath.split('H').join(' H '); 8 | newPath = newPath.split('h').join(' h '); 9 | newPath = newPath.split('V').join(' V '); 10 | newPath = newPath.split('v').join(' v '); 11 | newPath = newPath.split('Q').join(' Q '); 12 | newPath = newPath.split('q').join(' q '); 13 | newPath = newPath.split('Z').join(' Z '); 14 | newPath = newPath.split('z').join(' z '); 15 | newPath = newPath.split(',').join(' '); 16 | newPath = newPath.split(' ').join(' '); 17 | return newPath.trim(); 18 | }; 19 | 20 | export const isJsonString = (str: string) => { 21 | try { 22 | JSON.parse(str); 23 | } catch (e) { 24 | return false; 25 | } 26 | return true; 27 | }; 28 | 29 | export const getFontFileName = (metadata: string) => { 30 | return metadata.match(/[a-zA-Z0-9\[\]\-\,]*\.ttf/g); 31 | }; 32 | -------------------------------------------------------------------------------- /src/app/components/Layout/Layout.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { renderRoutes } from 'react-router-config'; 3 | import { Redirect, useLocation } from 'react-router-dom'; 4 | import styled from 'styled-components'; 5 | import Preview from '../Preview/Preview'; 6 | 7 | import { routeMap, routes } from '../routes'; 8 | import Footer from './Footer'; 9 | import Header from './Header'; 10 | 11 | const Layout = () => { 12 | const location = useLocation(); 13 | const isPreviewEnabled = 14 | routeMap.findIndex((route) => route.path === location.pathname && route.tab === true) !== -1; 15 | 16 | return ( 17 | 18 |
19 | 20 | {isPreviewEnabled && } 21 | 22 | {renderRoutes(routes)} 23 | 24 |