Comparing version 0.0.1 to 0.1.0
106
index.js
@@ -1,35 +0,29 @@ | ||
class figmaApi { | ||
_personalAccessToken; | ||
_fetchFunc; | ||
constructor({ personalAccessToken, fetchFunc }) { | ||
this._personalAccessToken = personalAccessToken; | ||
this._fetchFunc = fetchFunc; | ||
} | ||
async get(api, params) { | ||
const search = params | ||
? '?' + new URLSearchParams(params).toString() | ||
: ''; | ||
const response = await this._fetchFunc( | ||
`https://api.figma.com/v1/${api}${search}`, | ||
{ | ||
headers: { 'X-Figma-Token': this._personalAccessToken }, | ||
} | ||
); | ||
if (response.status >= 400) { | ||
let extra = ''; | ||
try { | ||
extra += (await response.json()).err; | ||
} finally { | ||
throw new Error( | ||
`HTTP error ${response.status} accessing Figma. ${extra}` | ||
); | ||
} | ||
function figmaApi({ personalAccessToken, fetchFunc }) { | ||
this._personalAccessToken = personalAccessToken; | ||
this._fetchFunc = fetchFunc; | ||
} | ||
figmaApi.prototype.get = async function (api, params) { | ||
const search = params ? '?' + new URLSearchParams(params).toString() : ''; | ||
const response = await this._fetchFunc( | ||
`https://api.figma.com/v1/${api}${search}`, | ||
{ | ||
headers: { 'X-Figma-Token': this._personalAccessToken }, | ||
} | ||
return response.json(); | ||
); | ||
if (response.status >= 400) { | ||
let extra = ''; | ||
try { | ||
extra += (await response.json()).err; | ||
} finally { | ||
throw new Error( | ||
`HTTP error ${response.status} accessing Figma. ${extra}` | ||
); | ||
} | ||
} | ||
} | ||
return response.json(); | ||
}; | ||
// For each nodeId, find its corresponding node by searching the document. | ||
// Note this is pretty inefficient, we can end up traversing the entire document quite a few times. Likely insignificant on smallish documents. | ||
function findNodesById({ file, nodeIds }) { | ||
findNodesById = function ({ file, nodeIds }) { | ||
function findNode(nodeId, searchNode) { | ||
@@ -47,5 +41,5 @@ if (nodeId === searchNode.id) { | ||
return nodeIds.map((nodeId) => findNode(nodeId, file.document)); | ||
} | ||
}; | ||
function findNodeIdsForNames({ file, names }) { | ||
findNodeIdsForNames = function ({ file, names }) { | ||
// find node IDs for names with a depth-first search | ||
@@ -65,3 +59,3 @@ function findNameInNode(name, node) { | ||
return names.map((name) => findNameInNode(name, file.document)); | ||
} | ||
}; | ||
@@ -122,3 +116,3 @@ /* Request images for some assets, and combine multiple scales into one object per asset */ | ||
*/ | ||
async function getFigmaIconsByFrames({ | ||
async function getFigmassets({ | ||
frameIds = [], | ||
@@ -130,3 +124,3 @@ frameNames = [], | ||
format = 'png', | ||
fetchFunc = window.fetch, | ||
fetchFunc = (...args) => window.fetch(...args), | ||
}) { | ||
@@ -151,2 +145,44 @@ const api = new figmaApi({ personalAccessToken, fetchFunc }); | ||
module.exports = { getFigmaIconsByFrames }; | ||
function addAssetsToMap(map, assets) { | ||
for (const iconId of Object.keys(assets)) { | ||
const scale = Math.max( | ||
...Object.keys(assets[iconId]) | ||
.filter((k) => k[0] === '@') | ||
.map((k) => +k.replace(/[^0-9.]/g, '')) | ||
); | ||
map.loadImage(assets[iconId][`@${scale}x`], (error, image) => { | ||
this.map.addImage(iconId, image, { | ||
pixelRatio: scale, | ||
}); | ||
}); | ||
} | ||
} | ||
async function loadFigmassets({ map, ...otherArgs }) { | ||
const assets = await getFigmassets(otherArgs); | ||
if (map) { | ||
addAssetsToMap(map, assets); | ||
} | ||
return assets; | ||
} | ||
// path is URL path to directory containing assets.json and every referenced image | ||
async function loadStoredFigmassets({ map, path = '' }) { | ||
if (path) { | ||
path = path.replace(/([^/])$/, '$1/'); | ||
} | ||
const assets = await fetch(`${path}assets.json`).then((r) => r.json()); | ||
for (const asset of assets) { | ||
map.loadImage(`${path}${asset.fileName}`, (error, image) => { | ||
this.map.addImage(asset.id, image, { pixelRatio: asset.scale }); | ||
}); | ||
} | ||
} | ||
module.exports = { | ||
getFigmaIconsByFrames: getFigmassets, | ||
getFigmassets, | ||
addAssetsToMap, | ||
loadFigmassets, | ||
loadStoredFigmassets, | ||
}; |
{ | ||
"name": "figmasset", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"main": "index.js", | ||
"files": ["index.js"] | ||
"files": [ | ||
"index.js" | ||
] | ||
} |
@@ -21,12 +21,16 @@ ## Figmasset | ||
![](layout.png) | ||
(Some of the assets are frames, some are groups - this is fine.) | ||
``` | ||
# myframe | ||
# mycircle | ||
# myrectangle | ||
# myotherframe | ||
# myexclamation | ||
- dot | ||
- straight bit | ||
# other frame I don't care about | ||
# junk | ||
# pins | ||
# pin | ||
# pin-cluster | ||
# pin-selected | ||
# pin-cluster-selected | ||
# pins-alt-clustering | ||
# pin-cluster | ||
# pin-cluster-selected | ||
``` | ||
@@ -36,16 +40,46 @@ | ||
### Loading your assets | ||
### Loading assets into Mapbox GL JS or Maplibre GL JS | ||
To retrieve your assets as icons and load them into a map, use `loadFigmassets()`, like this: | ||
```js | ||
import { getFigmaIconsByFrames } from 'figmasset'; | ||
import { loadFigmassets } from 'figmasset'; | ||
//... | ||
loadFigmassets({ | ||
map, // Mapbox GL JS or Maplibre GL JS object | ||
frameNames = ['pins', 'pins-alt-clustering'], | ||
fileKey: 'ABC123', | ||
personalAccessToken: 'snt34h5sn24h5', // get this from your user > Settings page. Be careful who you expose this to, it provides unrestricted access to your account | ||
scales: [2], // pixel ratios. [1,2] fetches both @1x and @2x versions of each asset. | ||
}); | ||
map.addLayer({ | ||
id: 'mypin', | ||
type: 'symbol', | ||
source: 'pointsource', | ||
layout: { 'icon-image': 'pin' } | ||
}); | ||
``` | ||
`loadFigmassets()` is async, returning after icons have been generated by Figma, and are being loaded into your map. There is generally no need to `await` it, although you may get warnings in your console if you create map layers that reference icons that haven't been loaded yet. | ||
### Loading your assets outside a map | ||
You can use Figmasset outside of a map (for instance, to overlay static images). It returns an object of all the | ||
```js | ||
import { getFigmassets } from 'figmasset'; | ||
async function loadAssets() { | ||
return getFigmaIconsByFrames({ | ||
// frameIds = [], // you can specify frames by their node ID (in the URL) | ||
frameNames = ['myframe', 'myotherframe'], | ||
return getFigmassets({ | ||
frameNames = ['pins', 'pins-alt-clustering'], | ||
fileKey: 'ABC123', | ||
personalAccessToken: 'snt34h5sn24h5', // get this from your user > Settings page. Be careful who you expose this to, it provides unrestricted access to your account | ||
scales: [1, 2], // pixel ratios. [1,2] fetches both @1x and @2x versions of each asset. | ||
// other options: | ||
// frameIds = [], // you can specify frames by their node ID (in the URL) instead of frameNames | ||
// format: 'png', // svg is also supported | ||
// fetchFunc: window.fetch, // in the browser, fetch is used. If using in Node, pass in the node-fetch library. | ||
// fetchFunc: nodeFetch // in the browser, window.fetch is used. If using in Node, pass in the node-fetch library. | ||
}); | ||
@@ -59,3 +93,3 @@ } | ||
{ | ||
'mycircle': { | ||
pin: { | ||
id: '2:8', // the node ID | ||
@@ -65,29 +99,21 @@ '@1x': 'https://s3-us-west-2.amazonaws.com/figma-alpha-api/img/5b83/a061/e42384a5bc5a5ac5cabcb5a5cabcb5c5, | ||
}, | ||
... | ||
'pin-cluster': { ... }, | ||
'pin-selected': { ... }, | ||
'pin-cluster-selected': { ... }, | ||
} | ||
``` | ||
### Loading assets into Mapbox GL JS or Maplibre GL JS | ||
Note that the assets named `pin-cluster` and `pin-cluster-selected` in the `pins-alt-clustering` frame will be used, rather than those in `pins`. That's because the names match, and `pins-alt-clustering` was specified after `pins`. | ||
To load the assets as icons, you can use this snippet: | ||
```js | ||
const assets = loadAssets(); // defined above | ||
for (const iconId of Object.keys(assets)) { | ||
map.loadImage(assets[iconId]['@2x'], (error, image) => { | ||
this.map.addImage(asset.id, image, { | ||
pixelRatio: asset.scale | ||
}) | ||
}); | ||
} | ||
``` | ||
### Moving to production | ||
or, if you are using map-gl-utils: | ||
Once your asset designs have stabilised you will want to move to production without loading them from Figma dynamically. You can use [`figmasset-export`](https://www.npmjs.com/package/figmasset-export) for this. The process is: | ||
1. Run figmasset-export to download assets and generate an asset metadata file, in a directory inside your web app. (Say its URL path is `static`, so you now have `static/assets@2x/assets.json` and so on). | ||
2. Switch out the `loadFigmassets()` call above with `loadStoredFigmassets()` like this: | ||
```js | ||
for (const iconId of Object.keys(assets)) { | ||
map.U.loadImage(iconId, assets[iconId]['@2x'], { | ||
pixelRatio: asset.scale, | ||
}); | ||
} | ||
loadStoredFigmassets({ map, path: 'static/assets@2x' }); | ||
``` |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
10480
170
115
1