-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from mfrances17/export-plugin
Add Figma export plugin
- Loading branch information
Showing
4 changed files
with
298 additions
and
0 deletions.
There are no files selected for viewing
22 changes: 22 additions & 0 deletions
22
packages/module/plugins/export-patternfly-tokens/README.md
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,22 @@ | ||
# Export Patternfly Tokens Plugin | ||
|
||
This plugin exports Patternfly tokens out of Figma and creates JSON files that can be added to the design tokens repo and processed by style dictionary. | ||
|
||
### Setup | ||
1. Clone the [design-tokens](https://github.com/patternfly/design-tokens) repository. | ||
2. Open the Figma app. | ||
3. In Figma, select **Plugins** > **Development** > **Import plugin from manifest**. | ||
4. Browse to **design-tokens\packages\module\plugins\export-patternfly-tokens** and select the **manifest.json** file from your cloned design-tokens repository and click **Open**. | ||
|
||
The Export Patternfly Tokens plugin should now be available to use as a development plugin in your Figma environment. | ||
|
||
### Usage | ||
Once the plugin has been added to Figma via the manifest file: | ||
1. In Figma, select **Plugins** > **Development** > **Export Patternfly Tokens** > **Export Tokens**. | ||
2. Click **Export Tokens**. The text area will display a concatenated list of all tokens exported from the Figma library. Links to each exported JSON file are displayed at the bottom of the dialog. | ||
3. Click each JSON file link to save them locally (do not rename the JSON files!). | ||
4. Copy the local JSON files to your cloned design-tokens repo: | ||
1. Copy **base.dimension.json**, **base.json**, **semantic.dimension.json**, **semantic.json**, and **palette.color.json** to **\packages\module\tokens\default**. | ||
2. Copy **base.dark.json**, **semantic.dark.json**, and **palette.color.json** to **\packages\module\tokens\dark** to **\packages\module\tokens\dark**. | ||
|
||
Note that **palette.color.json** is saved to both the **default** and **dark** directories. |
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,85 @@ | ||
console.clear(); | ||
|
||
/* MAIN function */ | ||
|
||
figma.ui.onmessage = (e) => { | ||
console.log("code received message", e); | ||
if (e.type === "IMPORT") { | ||
const { selectedCollection, selectedMode, body } = e; | ||
importJSONFile({ selectedCollection, selectedMode, body }); | ||
getExistingCollectionsAndModes(); | ||
} else if (e.type === "EXPORT") { | ||
exportToJSON(); | ||
} | ||
}; | ||
if (figma.command === "export") { | ||
figma.showUI(__uiFiles__["export"], { | ||
width: 820, | ||
height: 600, | ||
themeColors: true, | ||
}); | ||
} | ||
|
||
/* EXPORT Functionality */ | ||
|
||
/* EXPORT - main function */ | ||
|
||
function exportToJSON() { | ||
const collections = figma.variables.getLocalVariableCollections(); | ||
|
||
const files = []; | ||
collections.forEach((collection) => | ||
files.push(...processCollection(collection)) | ||
); | ||
|
||
figma.ui.postMessage({ type: "EXPORT_RESULT", files }); | ||
} | ||
|
||
/* EXPORT - helper functions */ | ||
|
||
function processCollection({ name, modes, variableIds }) { | ||
const files = []; | ||
modes.forEach((mode) => { | ||
let file = { fileName: `${name}.${mode.name}.tokens.json`, body: {} }; | ||
|
||
variableIds.forEach((variableId) => { | ||
const { name, resolvedType, valuesByMode } = | ||
figma.variables.getVariableById(variableId); | ||
const value = valuesByMode[mode.modeId]; | ||
|
||
if (value !== undefined && ["COLOR", "FLOAT"].includes(resolvedType)) { | ||
let obj = file.body; | ||
name.split("/").forEach((groupName) => { | ||
obj[groupName] = obj[groupName] || {}; | ||
obj = obj[groupName]; | ||
}); | ||
|
||
obj.$type = resolvedType === "COLOR" ? "color" : "number"; | ||
if (value.type === "VARIABLE_ALIAS") { | ||
obj.$value = `{${figma.variables | ||
.getVariableById(value.id) | ||
.name.replace(/\//g, ".")}}`; | ||
} else { | ||
obj.$value = resolvedType === "COLOR" ? rgbToHex(value) : value; | ||
} | ||
} | ||
}); | ||
files.push(file); | ||
}); | ||
return files; | ||
} | ||
|
||
function rgbToHex({ r, g, b, a }) { | ||
if (a !== 1) { | ||
return `rgba(${[r, g, b] | ||
.map((n) => Math.round(n * 255)) | ||
.join(", ")}, ${a.toFixed(4)})`; | ||
} | ||
const toHex = (value) => { | ||
const hex = Math.round(value * 255).toString(16); | ||
return hex.length === 1 ? "0" + hex : hex; | ||
}; | ||
|
||
const hex = [toHex(r), toHex(g), toHex(b)].join(""); | ||
return `#${hex}`; | ||
} |
179 changes: 179 additions & 0 deletions
179
packages/module/plugins/export-patternfly-tokens/export.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,179 @@ | ||
<!DOCTYPE html> | ||
<head> | ||
<style> | ||
:root { | ||
--spacing: 0.8rem; | ||
} | ||
|
||
* { | ||
box-sizing: border-box; | ||
} | ||
|
||
body { | ||
background-color: var(--figma-color-bg); | ||
color: var(--figma-color-text); | ||
margin: 0; | ||
padding: var(--spacing); | ||
} | ||
|
||
html, | ||
body, | ||
main { | ||
height: 98%; | ||
} | ||
|
||
main { | ||
display: flex; | ||
flex-direction: column; | ||
gap: var(--spacing); | ||
} | ||
|
||
button { | ||
appearance: none; | ||
border-radius: 4px; | ||
padding: var(--spacing); | ||
} | ||
|
||
textarea { | ||
background-color: var(--figma-color-bg-secondary); | ||
border: 2px solid var(--figma-color-border); | ||
color: var(--figma-color-text-secondary); | ||
flex: 1; | ||
font-family: Andale Mono, monospace; | ||
font-size: 0.9rem; | ||
overflow: auto; | ||
padding: var(--spacing); | ||
white-space: pre; | ||
} | ||
textarea:focus { | ||
border-color: var(--figma-color-border-selected); | ||
outline: none; | ||
} | ||
|
||
button, | ||
textarea { | ||
display: block; | ||
width: 100%; | ||
} | ||
|
||
a, | ||
p { | ||
color: var(--figma-color-text-secondary); | ||
padding-right: 5px; | ||
} | ||
|
||
button { | ||
background-color: var(--figma-color-bg-brand); | ||
color: var(--figma-color-text-onbrand); | ||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", | ||
Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", | ||
sans-serif; | ||
font-weight: bold; | ||
} | ||
|
||
button-container { | ||
display: flex; | ||
justify-content: space-around; | ||
} | ||
|
||
link-container { | ||
display: flex; | ||
justify-content: space-around; | ||
} | ||
|
||
#export { | ||
background-color: var(--figma-color-bg-component); | ||
} | ||
|
||
#save { | ||
background-color: var(--figma-color-bg-component); | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<main> | ||
<button-container> | ||
<button id="export" type="button">Export Tokens</button> | ||
</button-container> | ||
<textarea | ||
placeholder="All exported tokens will render here..." | ||
readonly | ||
></textarea> | ||
<link-spacer> | ||
<p>Click each exported json file to download:</p> | ||
</link-spacer> | ||
</main> | ||
<script> | ||
window.onmessage = ({ data: { pluginMessage } }) => { | ||
if (pluginMessage.type === "EXPORT_RESULT") { | ||
let saveFileName = ""; | ||
let textOutput = pluginMessage.files.map( ({ fileName, body }) => | ||
`/* ${fileName} */\n\n${JSON.stringify(body, null, 2)}` | ||
) | ||
.join("\n\n\n"); | ||
textOutput = textOutput | ||
.replaceAll("$type", "type") | ||
.replaceAll("$value", "value"); | ||
document.querySelector("textarea").innerHTML = textOutput; | ||
|
||
saveVars(textOutput, saveFileName); | ||
} | ||
|
||
function saveVars(text, fileName) { | ||
var splitFiles = text.split('\n\n\n'); | ||
|
||
for (var i = 0; i < splitFiles.length; i++) { | ||
var splitFileName = splitFiles[i].split('\n', 1)[0]; | ||
var saveFileName = ""; | ||
|
||
switch (splitFileName) { | ||
case "/* Base Dimension Tokens.Mode 1.tokens.json */": | ||
saveFileName = "base.dimension.json"; | ||
break; | ||
case "/* Base Color Tokens - Light.Value.tokens.json */": | ||
saveFileName = "base.json"; | ||
break; | ||
case "/* Color Palette.Mode 1.tokens.json */": | ||
saveFileName = "palette.color.json"; | ||
break; | ||
case "/* Semantic Dimension Tokens.Mode 1.tokens.json */": | ||
saveFileName = "semantic.dimension.json"; | ||
break; | ||
case "/* Semantic Color Tokens.Light.tokens.json */": | ||
saveFileName = "semantic.json"; | ||
break; | ||
case "/* Base Color Tokens - Dark.Mode 1.tokens.json */": | ||
saveFileName = "base.dark.json"; | ||
break; | ||
case "/* Semantic Color Tokens.Dark.tokens.json */": | ||
saveFileName = "semantic.dark.json"; | ||
break; | ||
default: | ||
saveFileName = splitFiles[i].split('\n', 1)[0] | ||
} | ||
|
||
const fileToExport = (splitFiles[i].substring(splitFiles[i].indexOf("\n") + 1) ); | ||
var textToSaveAsBlob = new Blob([fileToExport], {type:"text/plain"}); | ||
|
||
createLink(textToSaveAsBlob, saveFileName); | ||
} | ||
} | ||
|
||
function createLink(text, file) { | ||
var textToSaveAsURL = window.URL.createObjectURL(text); | ||
var downloadLink = document.createElement("a"); | ||
|
||
downloadLink.download = file; | ||
downloadLink.innerHTML = file; | ||
downloadLink.href = textToSaveAsURL; | ||
document.body.appendChild(downloadLink); | ||
} | ||
}; | ||
|
||
document.getElementById("export").addEventListener("click", () => { | ||
parent.postMessage({ pluginMessage: { type: "EXPORT" } }, "*"); | ||
}); | ||
|
||
</script> | ||
</body> | ||
</html> |
12 changes: 12 additions & 0 deletions
12
packages/module/plugins/export-patternfly-tokens/manifest.json
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,12 @@ | ||
{ | ||
"name": "Export Patternfly Tokens", | ||
"id": "1305931922901292580", | ||
"api": "1.0.0", | ||
"editorType": ["figma"], | ||
"permissions": [], | ||
"main": "code.js", | ||
"menu": [ | ||
{ "command": "export", "name": "Export Tokens" } | ||
], | ||
"ui": { "export": "export.html" } | ||
} |