Skip to content

Commit

Permalink
feat: png export (#24)
Browse files Browse the repository at this point in the history
* feat: png export

* chore: changeset
  • Loading branch information
junghyeonsu authored Nov 16, 2023
1 parent 26be5ec commit ae02a6f
Show file tree
Hide file tree
Showing 20 changed files with 247 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
"ignore": ["@icona/figma-plugin"]
}
7 changes: 7 additions & 0 deletions .changeset/shiny-berries-hope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@icona/generator": minor
"@icona/types": minor
"@icona/utils": minor
---

Add png export
Binary file not shown.
5 changes: 5 additions & 0 deletions figma-plugin/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,24 @@ export const ACTION = {
GET_GITHUB_REPO_URL: "get-github-repo-url",
GET_GITHUB_API_KEY: "get-github-api-key",
GET_ICON_PREVIEW: "get-icon-preview",
GET_DEPLOY_WITH_PNG: "get-deploy-with-png",

SET_GITHUB_REPO_URL: "set-github-repo-url",
SET_GITHUB_API_KEY: "set-github-api-key",
SET_DEPLOY_WITH_PNG: "set-deploy-with-png",

DEPLOY_ICON: "deploy-icon",
DEPLOY_ICON_STATUS: "deploy-icon-status",
DEPLOY_ICON_ERROR_MESSAGE: "deploy-icon-error-message",
} as const;

export const DATA = {
GITHUB_API_KEY: "github-api-key",
GITHUB_REPO_URL: "github-repo-url",

ICON_FRAME_ID: "icona-frame",

DEPLOY_WITH_PNG: "deploy-with-png",
} as const;

export const STATUS = {
Expand Down
9 changes: 9 additions & 0 deletions figma-plugin/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,22 @@ export interface GithubData {

export interface IconaMetaData {
githubData: GithubData;
options?: {
withPng?: boolean;
};
}

export type Status = `${(typeof STATUS)[keyof typeof STATUS]}`;

export type Messages =
| { type: `${typeof ACTION.GET_DEPLOY_WITH_PNG}`; payload: boolean }
| { type: `${typeof ACTION.GET_GITHUB_API_KEY}`; payload: string }
| { type: `${typeof ACTION.GET_GITHUB_REPO_URL}`; payload: string }
| {
type: `${typeof ACTION.GET_ICON_PREVIEW}`;
payload: Record<string, IconaIconData>;
}
| { type: `${typeof ACTION.SET_DEPLOY_WITH_PNG}`; payload: boolean }
| { type: `${typeof ACTION.SET_GITHUB_API_KEY}`; payload: string }
| { type: `${typeof ACTION.SET_GITHUB_REPO_URL}`; payload: string }
| { type: `${typeof ACTION.DEPLOY_ICON}`; payload: IconaMetaData }
Expand All @@ -31,4 +36,8 @@ export type Messages =
id: string;
name: string;
};
}
| {
type: `${typeof ACTION.DEPLOY_ICON_ERROR_MESSAGE}`;
payload: string;
};
2 changes: 2 additions & 0 deletions figma-plugin/esbuild.plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ const esbuild = require("esbuild");
const commonOpts = {
entryPoints: ["./plugin-src/code.ts"],
outfile: "dist/code.js",
target: "es6",
bundle: true,
minify: true,
plugins: [],
};

Expand Down
2 changes: 2 additions & 0 deletions figma-plugin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "@icona/figma-plugin",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "yarn build:ui && yarn build:plugin -- --minify",
"build:plugin": "node ./esbuild.plugin.js",
Expand All @@ -22,6 +23,7 @@
"@vitejs/plugin-react-refresh": "^1.3.6",
"axios": "^1.6.0",
"framer-motion": "^10.12.11",
"js-base64": "^3.7.5",
"june-so-sandbox-react": "^0.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
25 changes: 17 additions & 8 deletions figma-plugin/plugin-src/code.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ACTION, DATA, STATUS } from "../common/constants";
import type { Messages } from "../common/types";
import { createGithubClient } from "./github";
import { getSvgInIconFrame } from "./service";
import { getAssetInIconFrame } from "./service";

figma.showUI(__html__, { width: 360, height: 436 });

Expand Down Expand Up @@ -39,6 +39,10 @@ async function init() {
data: DATA.GITHUB_REPO_URL,
type: ACTION.GET_GITHUB_REPO_URL,
},
{
data: DATA.DEPLOY_WITH_PNG,
type: ACTION.GET_DEPLOY_WITH_PNG,
},
];

events.forEach((event) => {
Expand All @@ -57,7 +61,9 @@ async function init() {
figma.notify("Icona frame not found");
return;
} else {
const svgDatas = await getSvgInIconFrame(iconaFrame.id);
const svgDatas = await getAssetInIconFrame(iconaFrame.id, {
withPng: false,
});

figma.ui.postMessage({
type: ACTION.GET_ICON_PREVIEW,
Expand All @@ -78,12 +84,17 @@ figma.ui.onmessage = async (msg: Messages) => {
setLocalData(DATA.GITHUB_API_KEY, msg.payload);
break;

case ACTION.SET_DEPLOY_WITH_PNG:
setLocalData(DATA.DEPLOY_WITH_PNG, msg.payload);
break;

case ACTION.DEPLOY_ICON: {
figma.ui.postMessage({
type: ACTION.DEPLOY_ICON_STATUS,
payload: STATUS.LOADING,
});
const { githubData } = msg.payload;
const { githubData, options } = msg.payload;
const { withPng } = options ?? { withPng: true };

try {
const { owner, name, apiKey } = githubData;
Expand All @@ -93,7 +104,9 @@ figma.ui.onmessage = async (msg: Messages) => {
});

if (!iconaFrame) throw new Error("Icona frame not found");
const svgs = await getSvgInIconFrame(iconaFrame.id);
const svgs = await getAssetInIconFrame(iconaFrame.id, {
withPng,
});

await createDeployPR(svgs);
figma.ui.postMessage({
Expand All @@ -120,10 +133,6 @@ figma.ui.onmessage = async (msg: Messages) => {
}
break;
}

// case "cancel":
// figma.closePlugin();
// break;
}
};

Expand Down
22 changes: 18 additions & 4 deletions figma-plugin/plugin-src/service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/* eslint-disable @typescript-eslint/no-shadow */
import type { IconaIconData } from "@icona/types";
import { Base64 } from "js-base64";

type TargetNode =
| ComponentNode
Expand Down Expand Up @@ -68,12 +70,15 @@ const findComponentInNode = (
}
};

export async function getSvgInIconFrame(
export async function getAssetInIconFrame(
iconFrameId: string,
options?: {
withPng?: boolean;
},
): Promise<Record<string, IconaIconData>> {
const frame = figma.getNodeById(iconFrameId) as FrameNode;

console.log("frame", frame.children);
const withPng = options?.withPng ?? true;

const targetNodes = frame.children.flatMap((child) => {
if (
Expand All @@ -84,8 +89,6 @@ export async function getSvgInIconFrame(
child.type === "GROUP" ||
child.type === "COMPONENT_SET"
) {
console.log("child", child.type);

return findComponentInNode(child);
}
return [];
Expand All @@ -96,10 +99,21 @@ export async function getSvgInIconFrame(
const svgs = await Promise.all(
targetComponents.map(async (component) => {
const node = figma.getNodeById(component.id) as ComponentNode;

// svg
const svg = await node.exportAsync({
format: "SVG_STRING",
svgIdAttribute: true,
});

// png
if (withPng) {
const png = await node.exportAsync({
format: "PNG",
});
const base64String = Base64.fromUint8Array(png);
return { name: component.name, svg, png: base64String };
}
return { name: component.name, svg };
}),
);
Expand Down
12 changes: 7 additions & 5 deletions figma-plugin/plugin-src/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{
"compilerOptions": {
"target": "ESNext",
"lib": ["ESNext"],
"esModuleInterop": true,
"isolatedModules": true,
"jsx": "react",
"lib": ["DOM", "ES2020"],
"module": "ES2020",
"moduleResolution": "Node",
"strict": true,
"skipLibCheck": true,
"moduleResolution": "node",
"types": ["@figma/plugin-typings", "node"]
"types": ["@figma/plugin-typings"]
}
}
40 changes: 33 additions & 7 deletions figma-plugin/ui-src/contexts/AppContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type State = {
githubApiKey: string;

iconPreview: Record<string, IconaIconData>;
isDeployWithPng: boolean;

// Status
deployIconStatus: Status;
Expand All @@ -34,6 +35,12 @@ const AppDispatchContext = createContext<AppDispatch | null>(null);
function reducer(state: State, action: Messages): State {
switch (action.type) {
/* GETTER */
case ACTION.GET_DEPLOY_WITH_PNG: {
return {
...state,
isDeployWithPng: action.payload,
};
}
case ACTION.GET_USER_INFO: {
return {
...state,
Expand Down Expand Up @@ -66,6 +73,17 @@ function reducer(state: State, action: Messages): State {
};

/* SETTER */
case ACTION.SET_DEPLOY_WITH_PNG: {
postMessage({
type: action.type,
payload: action.payload,
});
return {
...state,
isDeployWithPng: action.payload,
};
}

case ACTION.SET_GITHUB_API_KEY:
postMessage({
type: action.type,
Expand Down Expand Up @@ -99,6 +117,9 @@ function reducer(state: State, action: Messages): State {
type: ACTION.DEPLOY_ICON,
payload: {
githubData: action.payload.githubData,
options: {
withPng: state.isDeployWithPng,
},
},
});
return state;
Expand Down Expand Up @@ -133,6 +154,7 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
// Input
githubApiKey: "",
githubRepositoryUrl: "",
isDeployWithPng: true,

// Status
deployIconStatus: STATUS.IDLE,
Expand All @@ -145,26 +167,30 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
window.onmessage = (event) => {
const msg = event.data.pluginMessage as Messages;
switch (msg.type) {
case ACTION.GET_DEPLOY_WITH_PNG: {
dispatch({ type: msg.type, payload: msg.payload });
break;
}
case ACTION.GET_USER_INFO: {
if (msg.payload) dispatch({ type: msg.type, payload: msg.payload });
return;
dispatch({ type: msg.type, payload: msg.payload });
break;
}
case ACTION.GET_GITHUB_API_KEY:
if (msg.payload) dispatch({ type: msg.type, payload: msg.payload });
dispatch({ type: msg.type, payload: msg.payload });
break;
case ACTION.GET_GITHUB_REPO_URL:
if (msg.payload) dispatch({ type: msg.type, payload: msg.payload });
dispatch({ type: msg.type, payload: msg.payload });
break;
case ACTION.GET_ICON_PREVIEW:
if (msg.payload) dispatch({ type: msg.type, payload: msg.payload });
dispatch({ type: msg.type, payload: msg.payload });
break;
case ACTION.DEPLOY_ICON_STATUS: {
dispatch({ type: msg.type, payload: msg.payload });
return;
break;
}
}
};
}, [dispatch]);
}, []);

return (
<AppStateContext.Provider value={state}>
Expand Down
Loading

0 comments on commit ae02a6f

Please sign in to comment.