-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Export salt theme plugin #4
Draft
origami-z
wants to merge
6
commits into
main
Choose a base branch
from
export-salt-theme
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
fdca42e
New export salt theme basic set up
origami-z 9595046
Modify key according to salt rule
origami-z 9284434
Fix build
origami-z b81e8d0
Fix camel casing token
origami-z b9b4de2
Bump salt version
origami-z fdcd39c
Update key mapping to support new structure
origami-z File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
![Plugin screenshot](docs/export-salt-theme-plugin-screenshot.png) | ||
|
||
Figma plugin to export Salt DS library to JSON object, which can be previewed using code. |
Binary file added
BIN
+145 KB
packages/export-salt-theme/docs/export-salt-theme-plugin-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"name": "Export Salt Theme", | ||
"id": "export-salt-theme", | ||
"api": "1.0.0", | ||
"editorType": ["figma"], | ||
"permissions": [], | ||
"main": "dist/code.js", | ||
"ui": "dist/index.html" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
{ | ||
"name": "figma-export-salt-theme", | ||
"version": "0.0.1", | ||
"description": "Figma plugin exports salt theme", | ||
"main": "dist/code.js", | ||
"scripts": { | ||
"tsc": "npm run tsc:main && npm run tsc:ui", | ||
"tsc:main": "tsc --noEmit -p plugin-src", | ||
"tsc:ui": "tsc --noEmit -p ui-src", | ||
"tsc:watch": "concurrently -n widget,iframe,tests \"npm run tsc:main -- --watch --preserveWatchOutput\" \"npm run tsc:ui -- --watch --preserveWatchOutput\" \"npm run tsc:tests -- --watch --preserveWatchOutput\"", | ||
"build": "npm run build:ui && npm run build:main -- --minify", | ||
"build:main": "esbuild plugin-src/code.ts --bundle --outfile=dist/code.js --target=es6", | ||
"build:ui": "npx vite build --minify esbuild --emptyOutDir=false", | ||
"build:watch": "concurrently -n widget,iframe \"npm run build:main -- --watch\" \"npm run build:ui -- --watch\"", | ||
"dev": "concurrently -n tsc,build,vite 'npm:tsc:watch' 'npm:build:watch' 'vite'" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/jpmorganchase/Figma-Plugins-and-Widgets.git" | ||
}, | ||
"keywords": [ | ||
"figma-plugin" | ||
], | ||
"author": "JPMC", | ||
"license": "Apache-2.0", | ||
"bugs": { | ||
"url": "https://github.com/jpmorganchase/Figma-Plugins-and-Widgets/issues" | ||
}, | ||
"homepage": "https://github.com/jpmorganchase/Figma-Plugins-and-Widgets#readme", | ||
"dependencies": { | ||
"@salt-ds/core": "^1.7.1", | ||
"@salt-ds/icons": "^1.3.1", | ||
"@salt-ds/lab": "^1.0.0-alpha.9", | ||
"@salt-ds/theme": "^1.5.0", | ||
"lz-string": "^1.5.0", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { PostToFigmaMessage, PostToUIMessage } from "../shared-src/messages"; | ||
import { generateThemeJson } from "./utils"; | ||
|
||
const WINDOW_MIN_WIDTH = 400; | ||
const WINDOW_MIN_HEIGHT = 500; | ||
|
||
figma.showUI(__html__, { | ||
themeColors: true, | ||
width: WINDOW_MIN_WIDTH, | ||
height: WINDOW_MIN_HEIGHT, | ||
}); | ||
|
||
figma.ui.onmessage = (msg: PostToFigmaMessage) => { | ||
if (msg.type === "ui-ready") { | ||
} else if (msg.type === "generate-json") { | ||
figma.ui.postMessage({ | ||
type: "generate-json-result", | ||
data: generateThemeJson(), | ||
} satisfies PostToUIMessage); | ||
} else if (msg.type === "resize-window") { | ||
const { width, height } = msg; | ||
figma.ui.resize( | ||
Math.max(width, WINDOW_MIN_WIDTH), | ||
Math.max(height, WINDOW_MIN_HEIGHT) | ||
); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ | ||
module.exports = { | ||
preset: "ts-jest", | ||
testEnvironment: "node", | ||
transform: { | ||
// Below matches default jest transform (from --debug) | ||
// or a second transform would be applied and tsconfig would be overridden | ||
"^.+\\.tsx?$": [ | ||
"ts-jest", | ||
{ | ||
tsconfig: "plugin-src/__tests__/tsconfig.json", | ||
}, | ||
], | ||
}, | ||
setupFiles: ["<rootDir>/../../../jest/globals.js"], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "es6", | ||
"lib": ["es6"], | ||
"strict": true, | ||
"typeRoots": ["../../../node_modules/@figma"] | ||
}, | ||
"include": ["./", "../shared-src"], | ||
"exclude": ["__tests__"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
type ThemeObject = any; | ||
type TokenType = any; | ||
|
||
export function setNestedKey(obj: any, path: string[], value: any): any { | ||
if (path.length === 0) { | ||
return obj; | ||
} else if (path.length === 1) { | ||
return { | ||
...obj, | ||
[path[0]]: value, | ||
}; | ||
} else { | ||
if (obj[path[0]]) { | ||
return { | ||
...obj, | ||
[path[0]]: setNestedKey(obj[path[0]], path.slice(1), value), | ||
}; | ||
} else { | ||
return { | ||
...obj, | ||
[path[0]]: setNestedKey({}, path.slice(1), value), | ||
}; | ||
} | ||
} | ||
} | ||
|
||
const KEY_MAP = new Map([["border", "borderColor"]]); | ||
|
||
/** | ||
* https://stackoverflow.com/a/2970667 | ||
**/ | ||
function camelize(str: string) { | ||
return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function (match, index) { | ||
if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces | ||
return index === 0 ? match.toLowerCase() : match.toUpperCase(); | ||
}); | ||
} | ||
|
||
/** Design side has different naming convention than code, need to modify the path to fit. */ | ||
export const fixPathForSalt = (path: string[]) => { | ||
return path.reduce( | ||
(prev, current, index, fullArray) => { | ||
const key = current.toLowerCase(); | ||
|
||
const mappedKeys = key.split(" ").map((x) => KEY_MAP.get(x) || x); | ||
|
||
// Ignore default when at last position | ||
if ( | ||
mappedKeys.length === 1 && | ||
mappedKeys[0] === "default" && | ||
fullArray.length - 1 === index | ||
) { | ||
return prev; | ||
} else { | ||
return [...prev, ...mappedKeys]; | ||
} | ||
}, | ||
// prefix salt as top level namespace if not existed | ||
path[0] === "salt" ? [] : ["salt"] | ||
); | ||
}; | ||
|
||
export const updateTheme = ( | ||
theme: ThemeObject, | ||
newToken: TokenType, | ||
path: string[] | ||
): ThemeObject => { | ||
let newTheme = { ...theme }; | ||
|
||
const newPath = fixPathForSalt(path); | ||
|
||
console.log({ newPath }); | ||
|
||
newTheme = setNestedKey(newTheme, newPath, newToken); | ||
|
||
return newTheme; | ||
}; | ||
|
||
// Refer to MDN font-weight page for more information. | ||
// https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight#common_weight_name_mapping | ||
export const FONT_WEIGHT_MAPPING: { [key: string]: number } = { | ||
Thin: 100, | ||
"Extra Light": 200, | ||
ExtraLight: 200, | ||
Light: 300, | ||
Regular: 400, | ||
Medium: 500, | ||
"Semi Bold": 600, | ||
SemiBold: 600, | ||
Semibold: 600, | ||
Bold: 700, | ||
"Extra Bold": 800, | ||
ExtraBold: 800, | ||
Extrabold: 800, | ||
Black: 900, | ||
}; | ||
|
||
// Try convert to valid CSS line height, see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height | ||
function extractLineHeight(lineHeight: LineHeight): string { | ||
switch (lineHeight.unit) { | ||
case "AUTO": { | ||
return "normal"; | ||
} | ||
case "PERCENT": { | ||
return Math.round(lineHeight.value) + "%"; | ||
} | ||
case "PIXELS": { | ||
return Math.round(lineHeight.value) + "px"; | ||
} | ||
} | ||
return "normal"; | ||
} | ||
|
||
export function generateThemeJson(): ThemeObject { | ||
// Just support raw color without reference for now | ||
|
||
let newTheme: ThemeObject = {}; | ||
|
||
const localPaintStyles = figma.getLocalPaintStyles(); | ||
for (let index = 0; index < localPaintStyles.length; index++) { | ||
const paintStyle = localPaintStyles[index]; | ||
if ( | ||
paintStyle.paints.length === 1 && | ||
paintStyle.paints[0].type === "SOLID" | ||
) { | ||
const objPaths = paintStyle.name.split("/"); | ||
|
||
const { color, opacity } = paintStyle.paints[0]; | ||
|
||
const newColorToken = { | ||
$type: "color", | ||
$value: { | ||
r: Math.round(color.r * 255), | ||
g: Math.round(color.g * 255), | ||
b: Math.round(color.b * 255), | ||
a: opacity ? Math.round(opacity * 100) / 100 : undefined, | ||
}, | ||
}; | ||
|
||
newTheme = updateTheme(newTheme, newColorToken as any, objPaths); | ||
} | ||
} | ||
|
||
const localTextStyles = figma.getLocalTextStyles(); | ||
for (let index = 0; index < localTextStyles.length; index++) { | ||
const textStyle = localTextStyles[index]; | ||
|
||
// NOTE: not all information is extracted | ||
const { fontName, fontSize, lineHeight } = textStyle; | ||
const { family, style } = fontName; | ||
|
||
const newTypographyToken = { | ||
$type: "typography", | ||
$value: { | ||
fontFamily: family, | ||
fontWeight: FONT_WEIGHT_MAPPING[style] || 400, // default to 400 / regular | ||
fontSize: fontSize + "px", // Figma fontSize is unit-less | ||
lineHeight: extractLineHeight(lineHeight), | ||
}, | ||
}; | ||
|
||
const objPaths = textStyle.name.split("/"); | ||
newTheme = updateTheme(newTheme, newTypographyToken as any, objPaths); | ||
} | ||
|
||
return newTheme; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./messages"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
export type GenerateJsonResultToUIMessage = { | ||
type: "generate-json-result"; | ||
data: any; | ||
}; | ||
|
||
export type PostToUIMessage = GenerateJsonResultToUIMessage; | ||
|
||
export type GenerateJsonToFigmaMessage = { | ||
type: "generate-json"; | ||
}; | ||
|
||
export type UIRedayToFigmaMessage = { | ||
type: "ui-ready"; | ||
}; | ||
|
||
export type ResizeWindowToFigmaMessage = { | ||
type: "resize-window"; | ||
width: number; | ||
height: number; | ||
}; | ||
|
||
export type PostToFigmaMessage = | ||
| GenerateJsonToFigmaMessage | ||
| UIRedayToFigmaMessage | ||
| ResizeWindowToFigmaMessage; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
empty code branch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah my bad .. pr still draft :)