Oletetaan, että sinulla on joukko kuvakkeita Figmassa ja projekti Reactissa/Vuessa/missä tahansa muussa. Ei yllätys, sinun on käytettävä näitä kuvakkeita. Kuinka voimme tuoda ne maahan?
Jos olet tarpeeksi itsepäinen, voit ladata ne yksitellen ja lisätä ne omaisuudeksi. Mutta se on surullinen tapa. Ei samankaltaisuutta.
Lopetetaan surullinen ja olla sen sijaan mahtavia. Tarvitsemme tässä:
*ei enempää puhetta*
Tarvitsemme Figma-tunnuksen. Mielestäni ei ole tarpeen selittää, miten tämä tehdään, joten tässä on sen sijaan ohjeosio: https://help.figma.com/hc/en-us/articles/8085703771159-Manage-personal-access-tokens .
Oletusasetukset ovat riittävän hyvät: tarvitsemme vain luku -oikeuden tiedoston sisältöön.
Käytän materiaalisuunnittelukuvakkeita (yhteisö) esimerkkinä.
Tarvitsemme tiedostoavaimen ja pääsolmun tunnuksen:
Nyt kun meillä on tarpeeksi vaatimuksia, aloitetaan koodaus. Pidän skripteistäni erotettuina pääprojektin koodista, joten luon scripts
juureen ja fetch-icons.ts
siihen. Sitä ei tarvitse tehdä TypeScriptillä, mutta se tekee tästä artikkelista hienon, joten jatkan sitä.
Tämä vaatii kuitenkin vielä pari riippuvuutta:
yarn add -D tsx dotenv axios @figma/rest-api-spec
dotenv
on täällä käyttääkseen .env
-muuttujia, tsx
— TS-komentosarjan suorittamiseen ( @figma/rest-api-spec
on tyyppiturvallisuus, ja axios
on vain siksi, että olen tottunut siihen. Ainoa vaadittu on dotenv
(ellet ole ok API-avaimen lisääminen koodiin), ja lisään "puhtaan" version loppuun. Toistaiseksi meillä on tämä kokoonpano, ja aloitamme seuraavasti:
import * as dotenv from "dotenv"; dotenv.config(); const PERSONAL_ACCESS_TOKEN = String( process.env.VITE_FIGMA_PERSONAL_ACCESS_TOKEN ); // I've added my Figma token to the .env file, and so should you const FIGMA_API_URL = "https://api.figma.com/v1"; const FILE_KEY = "v50KJO82W9bBJUppE8intT"; // this one is from the URL const NODE_ID = "2402-2207"; // node-id query param, also from the URL
Aluksi saamme pääkehyksen metatiedot:
import { GetFileNodesResponse, GetImagesResponse, HasChildrenTrait, } from "@figma/rest-api-spec"; // ... const fetchFrameData = async ( nodeId: string ): Promise<HasChildrenTrait> => { const { data } = await axios.get<GetFileNodesResponse>( `${FIGMA_API_URL}/files/${FILE_KEY}/nodes?ids=${nodeId}`, getConfig() ); return data.nodes[nodeId].document as HasChildrenTrait; };
Se palauttaa tiedot lapsistaan. Ne voivat olla kuvia (niiden tyyppi on INSTANCE
) tai muita kehyksiä, jotka jäsennämme rekursiivisesti:
const getImageNodes = ( frameData: HasChildrenTrait ): Record<string, string> => { const nodes: Record<string, string> = {}; for (const image of frameData.children.filter( (node) => node.type === 'INSTANCE' )) { // normalizeName simply converts 'check-box' to 'CheckBox' const name = normalizeName(image.name); const id = image.id; nodes[id] = name; } for (const frame of frameData.children.filter( (node) => node.type === 'FRAME' )) { Object.assign(nodes, getImageNodes(frame)); } return nodes; };
Lopuksi, kun meillä on joukko kuvatunnuksia, haemme SVG-tiedostojen URL-osoitteet:
const fetchImageUrls = async ( nodeIds: string[] ): Promise<GetImagesResponse> => { const { data } = await axios.get<GetImagesResponse>( `${FIGMA_API_URL}/images/${FILE_KEY}?ids=${nodeIds.join(",")}&format=svg`, getConfig() ); return data; };
Ja päädymme joukkoon sellaisia objekteja:
{ name: 'CheckBox', url: 'https://figma-alpha-api.s3.us-west-2.amazonaws.com/images/7dd5bc31-4f26-4701-b155-7bf7dea45824' }
Kuten olet jo arvannut, lataamme ne kaikki, mutta se ei ole kiinnostavin osa.
Joka kerta kun haemme SVG:n, saamme jotain tällaista:
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M17 6H7C3.69 6 1 8.69 [...] 9 17 9Z" fill="black" /> </svg>
Poistamme svg
-tunnisteet, koska ne ovat identtisiä jokaiselle kuvakkeelle (oletan, että ne ovat samankokoisia yksinkertaisuuden vuoksi). Mutta mielenkiintoisempaa on, että korvaamme kaikki fill=”black”
paikkamerkitsimellä mallin literaalissa, ja sen seurauksena meillä on tällainen toiminto:
export const IconToggleOn = (color: string) => `<path d="M17 6H7C3.69 [...] 9 17 9Z" fill="${color}"/>`;
Näin saamme sen:
const cleanupSvg = (data: string): string => { const svgTags = /<\/?svg.*?>/g; const colorFills = /fill=["'].+?["']/g; return data .replace(svgTags, "") .replace(colorFills, 'fill="${color}"') .replace(/\n/g, ""); };
Ilmeisesti sinun ei tarvitse vaihtaa värejä, jos sinun on tuotava joitain logoja, mutta erilaisen logiikan luominen niille on kotitehtäväsi.
Okei, tässä on suunnitelmamme:
En toista koko tiedostoa – voit tarkistaa sen GitHubista . Katsotaanpa itse latausta:
const FILE_BATCH_SIZE = 10; const fileContent: string[] = []; const getPromise = async (item: IconData) => { try { const { data } = await axios.get<string>( item.url, getConfig("image/svg+xml") // returns necessary options and headers ); return { name: item.name, data }; } catch (err) { console.error(err); } }; for (let i = 0; i < iconsData.length; i += FILE_BATCH_SIZE) { const batch = await Promise.allSettled( iconsData.slice(i, i + FILE_BATCH_SIZE).map(getPromise) ); for (const icon of batch) { if (icon?.status === "fulfilled" && icon.value) { const iconName = `Icon${icon.value.name}`; const svgData = cleanupSvg(icon.value.data); fileContent.push( `export const ${iconName} = (color: string) =>\n \`${svgData}\`;\n` ); } } }
Eräkäyttö on tärkeää, koska muuten Figma ei pidä sinusta, jos päätät ladata pari tuhatta tiedostoa rinnakkain.
Jos haluat saada kuvakkeen taustajärjestelmästä saamasi nimellä, voit myös luoda sanakirjan:
const iconArray: string[] = []; // ... for (const icon of batch) { if (icon?.status === "fulfilled" && icon.value) { const iconName = `Icon${icon.value.name}`; const svgData = cleanupSvg(icon.value.data); fileContent.push( `export const ${iconName} = (color: string) =>\n \`${svgData}\`;\n` ); iconArray.push(iconName); // <-- we added this } } // ... when all files are processed fileContent.push( `\r\nexport const iconDictionary: Record<string, (color: string) => string> = {\r\n ${iconArray.join( ",\r\n " )}\r\n};\r\n` );
Tämä lähestymistapa on huonompi, koska koko sarja lisätään nippuun, vaikka käytät vain yhtä kuvaketta sanakirjan kautta, mutta joskus sinun on tehtävä jotain epämiellyttävää. Lisäksi kaikki \r\n
ja välilyönnit ovat täällä tehdäkseni ESLintini onnelliseksi: saatat joutua säätämään niiden määrää.
Tässä tulee viimeinen vaihe; tehdään uusi tiedosto:
import path from "path"; import { fileURLToPath } from "url"; // ... const PATH_TO_ICONS = "../src/assets/icons"; const currentPath = fileURLToPath(import.meta.url); const currentDirectory = path.dirname(currentPath); const fileName = "index.ts"; const filePath = path.join( path.resolve(currentDirectory, PATH_TO_ICONS), fileName ); // ... fs.writeFileSync(filePath, fileContent.join(""));
On hyvä idea lajitella kuvakkeet URL-osoitteiden noutamisen jälkeen, mikä on näin yksinkertaista:
iconsData.sort((a, b) => a.name.localeCompare(b.name));
Lajiteltu luettelo on helpompi lukea, ja mikä tärkeintä, meillä on luettavissa oleva ero aina kun saamme uusia kuvakkeita. Muuten tilausta ei taata, ja yhden kuvakkeen lisääminen voi sotkea kaiken.
Tarvitsemme komponentin, joka hyväksyy kuvakejonon, värin ja koon rekvisiittana ja piirtää jotain makeaa:
import React, { useMemo } from "react"; import { IconSvg } from "../types/IconSvg"; interface IconProps { svg: IconSvg; // type IconSvg = (color: string) => string; color?: string; size?: number | string; } const refine = (x: string | number) => { return typeof x === "number" || !/\D+/.test(x) ? `${x}px` : x; }; const MyIcon: React.FC<IconProps> = ({ svg, color, size, }) => { const iconColor = useMemo(() => color ?? "black", [color]); const refinedSize = useMemo(() => refine(size ?? 24), [size]); const currentIcon = useMemo(() => svg(currentColor), [svg, currentColor]); return ( <svg viewBox="0 0 24 24" // make sure it's the same as in Figma dangerouslySetInnerHTML={{ __html: currentIcon }} style={{ width: refinedSize, minWidth: refinedSize, height: refinedSize, minHeight: refinedSize, }} ></svg> ); }; export default MyIcon;
Mielestäni se on melko suoraviivaista, paitsi koon tarkentaminen: kun otin sen käyttöön ensimmäistä kertaa, Chrome hyväksyi numerot leveys/korkeus CSS-ominaisuuksiksi, mutta Firefox ei, joten lisäsin px
. Vaikka asia ei enää olisikaan, pidetään se sellaisena kuin sen standardien mukaan kuuluu olla.
Ja tässä lupaamani automaattinen täydennys:
Näin teen sen. Toivottavasti löydät sen hyödylliseksi.
Tämän lähestymistavan haittapuolena on, että kuvakkeet ovat upotettuja eikä välimuistissa sen vuoksi. En kuitenkaan usko, että se on iso juttu muutamalla tavulla. Lisäksi voit lisätä pyöriviä, värin vaihtamista leijuttaessa, mitä tahansa muuta, ja sinulla on pieni Sveitsin armeijan veitsi näistä ikoneista.
Lisäksi se vaatii järjestetyn tiedoston Figmassa. Mutta hei, jos osaat kirjoittaa kaiken tämän koodin, voit myös suostutella suunnittelijasi selvittämään sen.
Löydät koko koodin, mukaan lukien React- ja Vue-komponentit, sekä Node.js-version, jossa on vähemmän riippuvuuksia, täältä: https://github.com/Smileek/fetch-icons