More

    Własny plugin do Figmy w React.js

    Jeśli udało Ci się kiedykolwiek pracować z Figmą, istnieje spora szansa, że narzędzie przypadło Ci do gustu. Tak przynajmniej było w moim wypadku. A co jest lepsze od darmowego narzędzia do tworzenia UI dostępnego w przeglądarce? Tego typu narzędzie umożliwiające tworzenie własnych rozszerzeń! Właśnie to dzisiaj zrobimy. Stworzymy własne rozszerzenie do Figmy od zera. Zanim zakasamy rękawy, wyjaśnijmy czym jest Figma i dlaczego stała się taka popularna.

    Figma to narzędzie do projektowania interfejsów użytkownika, które wspiera tworzenie aplikacji przeglądarkowych, mobilnych i innych za pomocą narzędzi do tworzenia i prototypowania. Przed Figmą nie istniało żadne sensowne rozwiązanie online, a najpopularniejsza aplikacja desktopowa, czyli Sketch jest dostępna tylko dla użytkowników komputerów z systemem MacOS. Bezpośrednim rywalem dla Figmy jest Adobe XD. Aplikacje są bardzo podobne, jednak subiektywnie wygodniejsza wydaje mi się Figma.

    Opis projektu

    Spróbujemy stworzyć rozszerzenie do Figmy pozwalające na określenie dla danego obszaru wyznaczenie ostatecznej daty realizacji (z ang. deadline). Jest to bardzo pomocne w przypadku, gdy pracujemy nad projektem aplikacji w większym gronie, a gonią nas terminy lub jeśli lubimy mieć dobrze zorganizowaną pracę. Dzięki temu osoby odpowiadające za UX i UI oraz programiści będą mogli lepiej zaplanować sobie czas realizacji konkretnych zadań. Jeśli to Cię wystarczająco zmotywowało to komputery w dłoń i do dzieła!

    Konfiguracja pluginu

    Przed rozpoczęciem pracy nad pluginem będziemy potrzebowali:

    • Visual Studio Code (lub dowolne IDE wspierające TypeScript)
    • NPM
    • Figma Desktop
    • TypeScript

    Aktualna konfiguracja znajduje się pod adresem https://www.figma.com/plugin-docs/setup/.

    Po zainstalowaniu IDE, Figmy i NPM-a (mam nadzieję, że masz to już za sobą) odpalamy komendę: 

    npm i -g typescript

    Tworzymy nowy plugin z poziom aplikacji Figma. Zostanie dla nas wygenerowany projekt zawierający plik manifest.json, który jest plikiem konfiguracyjnym dla Figmy oraz pliki przykładowe, które pozwolą nam na rozpoczęcie pracy nad rozszerzeniem.

    Teraz otwórzmy konsole w folderze, gdzie znajduje się nasze nowo stworzone rozszerzenie i dodajmy wsparcie dla typów Figmy w TypeScriptcie, a następnie włączmy IDE.

    npm install --save-dev @figma/plugin-typings
    code .

    W IDE zbudujmy pliki TypeScript z nasłuchiwaniem na zmiany. Niestety, bez włączonej konwersji plików .ts plugin nie będzie działał w Figmie. Możemy to zrobić w łatwy sposób przez Terminal > Run Build Task > tsc: watch. Sprawdźmy w Figmie czy aplikacja testowa działa poprawnie. Pierwszy krok milowy za nami. Mamy działające rozwiązanie!

    Budowanie rozszerzenia i uruchamianie

    Wdrożenie Reacta i Webpacka

    Aby ułatwić nam proces pisania kodu, wrzucimy do projektu Webpack i Reacta. RObimy to, ponieważ będziemy korzystać z zewnętrznej biblioteki do dat date-fns i react-datepicker.

    Zacznijmy od dodania do naszego projektu wszystkiego czego potrzebujemy

    npm install --save-dev css-loader html-webpack-inline-source-plugin@1.0.2TODO html-webpack-plugin style-loader ts-loader typescript url-loader webpack webpack-cli @types/react @types/react-dom @types/luxon
    npm install react react-dom luxon date-fns

    Musimy przenieść pliki ui.html, ui.ts i code.ts do folderu src oraz odpowiednio skonfigurować webpack.config.js (konwersja plików do wykonywalnego skryptu), a także manifest.json (modyfikacja ścieżek docelowych na wygenerowane pliki przez Webpack). Pełna instrukcja w artykułach: Bundling with Webpack · Figma Developers oraz Bundling React · Figma Developers lub do sprawdzenia plików w repozytorium na GitHubie rozszerzenia.

    Aby móc korzystać ze zmian przebudownanych przez Webpack w Figmie, musimy zmodyfikować package.json. Niestety, instrukcja z oficjalnej dokumentacji nie jest przygotowana dla użytkowników Windowsa, więc jeśli tak jak ja korzystasz z systemu giganta z Redmont, to wrzuć poniższy skrypt do pliku package.json, zmieniając ścieżkę na swoją lokalizację node.js’a (nie działa PATH):

      "build": "C:\\Users\\root\\AppData\\Roaming\\nvm\\v10.16.3\\node node_modules\\webpack\\bin\\webpack.js --mode production"

    Po powyższych zmianach nasz główny komponent będziemy tworzyć modyfikując ui.ts na ui.tsx oraz importując potrzebne nam biblioteki (date-fns).

    Proces tworzenia i integracji z API Figmy

    Figma udostępnia API, które jest we wstecznej fazie. Aktualnie wspierane funkcjonalności wymienione są na stronie https://www.figma.com/plugin-docs/whats-supported/. Niestety, kategoria Trigger plugin code on events ma zostać udostępniona w przyszłości, jednak teraz musimy każdorazowo uruchamiać plugin ręcznie. Zerknij na kod, który w przyszłości powinien pozwolić nam na wyświetlenie pluginu po zaznaczeniu zakresu:

    const isFrameSelected = () => figma.currentPage.selection[0] &&
      figma.currentPage.selection[0].type === 'FRAME';
    
    figma.on("selectionchange", () => {
        if (isFrameSelected()) {
          // Otwieranie kodu komponentu
        }
      }
    );
    

    Do naszego rozwiązania będziemy potrzebowali następujących składowych:

    1. Wyświetlenie opcji pluginu na zakresach (z ang. frames)
    2. Zapisanie wartości dla konkretnego zakresu (data do zatwierdzenia i status)
    3. Przeładowanie statusów podczas uruchomienia pluginu i wyświetlanie o czytelnej formie
    4. Podgląd zaległych zadań i “na dzisiaj”

    1. Wyświetlenie opcji pluginu dla zakresów (z ang. frames) w panelu

    W celu wyświetlenia akcji pluginu w panelu ustawień musimy dokonać dwóch zmian. Znacznie mniej niż Ci się wydaje. Po pierwsze, do manifest.json dorzucamy na koniec JSONa tablicę relaunchButtons z elementami zawierającymi: 

    • komendę wywołującą w kodzie
    • nazwę
    • opcjonalną flagę określającą czy przycisk ma się pojawiać w momencie gdy więcej elementów jest zaznaczonych na raz
     "relaunchButtons": [
            {"command": "edit", "name": "Edit shape"},
            {"command": "open", "name": "Open Shaper", "multipleSelection": true}
       ]
    ;

    Po drugie, żeby akcje się wyświetlały, muszą zostać przypięte do elementu. Plugin podczas uruchomienia doda nasze przyciski gdy na elemencie aktywnej strony wywołamy poniższy kod.

    figma.currentPage.setRelaunchData({ edit: 'Edit this trapezoid with Shaper', open: '' })

    2. Zapisanie wartości dla konkretnego zakresu (data do zatwierdzenia)

    Podczas uruchomienia pluginu będziemy ukrywać dialog pluginu, wyświetlać komunikat i oczekiwać na zaznaczenie zakresu.

    figma.showUI(__html__, {visible: false});
    figma.notify('Select frame or click on right panel Create Sample button')
    figma.on("selectionchange", () => {
        if (isFrameSelected()) {
          figma.currentPage.selection[0].setRelaunchData({ sample: 'Create sample frame', open: '' })
          figma.showUI(__html__, {width: 265, height: 500});
        }
      }
    );
    

    Do komponentu za pomocą postMessage będziemy przekazywać dane o wybranym fragmencie i obsługiwać zmianę za pomocą useEffect wykorzystując funkcję dostarczoną przez Figmę nazywającą się onmessage. W ten sposób będziemy komunikować się z aplikacją w naszym dialogu.

    figma.ui.postMessage({id: figma.currentPage.selection[0].id, name: figma.currentPage.selection[0].name});
    
        React.useEffect(() => {
          onmessage = (event) => {
            setMsg(event.data.pluginMessage.name);
          };
        }, []);
    

    3. Przeładowanie statusów podczas uruchomienia pluginu i wyświetlanie o czytelnej formie 

    Jeśli chcemy aby dane przeładowały się w trakcie uruchomienia rozszerzenia, to funkcję musimy wywoływać na najwyższej warstwie kodu w pliku code.ts. Wyszukujemy więc wszystkie wystąpienia node z nazwą Frame Deadline Text oraz sprawdzamy przy użyciu funkcji z biblioteki date-fns czy data zamieszczona znajduje się w przeszłości. Następnie przypisujemy w czytelny sposób czas do określonej daty.

    const refresh = () => {
     
      const deadlines = figma.currentPage.findAll((node)=> node.name === "Frame Deadline Text");
      console.log('deadlines', deadlines.map((node:TextNode) => {
        const date = node.getPluginData(PLUGIN_DATE);
        node.characters = formatDistance(new Date(`${date}`), new Date(), {addSuffix: true});
        if (isPast(new Date(`${date}`))) {
          const background = node.parent.findOne(background => background.name === 'Frame Deadline Background')
          if (background.type === 'RECTANGLE') {
            background.fills = [{type: 'SOLID', color: {r: 251/255, g: 132/255, b: 132/255}}];
          }
        }
        return date;
      }));
      figma.currentPage.setPluginData(LAST_UPDATE, new Date().toDateString());
     
    }
     
    refresh()
    ;

    4. Podgląd zaległych zadań i “na dzisiaj”

    W celu wyświetlenia tych informacji, stworzymy prosty dialog bez używania Reacta. Dialogi możemy tworzyć za pomocą figma.showUi (komponent w formie tekstowej, opcje rozdzielczości dialogu). 

     
    const countDeadlines = () => {
      const pastDeadlines = [];
      const todayDeadlines = [];
      const inThreeDaysDeadlines = [];
      const inWeekDeadlines = [];
      const futureDeadlines = [];
      const deadlines = figma.currentPage.findAll((node)=> node.name === "Frame Deadline Text");
      console.log('countDeadlines', deadlines)
      deadlines.forEach((node:TextNode) => {
        const date = node.getPluginData(PLUGIN_DATE);
        if (isPast(new Date(`${date}`))) {
          pastDeadlines.push(date);
        } else if (isToday(new Date(`${date}`))) {
          todayDeadlines.push(date);
        } else if (isBefore(new Date(`${date}`), endOfDay(addDays(new Date(), 3)))) {
          inThreeDaysDeadlines.push(date);
        } else if (isBefore(new Date(`${date}`), endOfDay(addDays(new Date(), 7)))) {
          inWeekDeadlines.push(date);
        } else {
          futureDeadlines.push(date);
        }
      });
        return `
          <div>
            <h3>Past: ${pastDeadlines.length}</h3>
            <h3>Today: ${todayDeadlines.length}</h3>
            <h3>In 3 days: ${inThreeDaysDeadlines.length}</h3>
            <h3>In week: ${inWeekDeadlines.length}</h3>
            <h3>Future: ${futureDeadlines.length}</h3>
          </div>
        `;
    }
     
    if (figma.command === 'stats') {
      figma.showUI(`<div>
      <h1>Deadlines statistics:</h1>
      ${countDeadlines()}
    </div>`, {width: 200, height: 320});
    }
    

    Raport z informacją w jakich przedziałach mamy zakresy czasowe:

    Podgląd dialogu raportu aktualnych zakresów posiadających przedział czasu

    Publikowanie rozszerzenie na platformie Figmy

    Nasze rozszerzenie możemy już opublikować na platformie Figma. Żeby to zrobić, musimy mieć stworzony profil. Warto wcześniej przygotować ikonę i baner, bo te elementy będą widoczne w sklepie z aplikacjami dla Figmy.

    Wypełniamy podstawowe informacje w oknie dialogowym do publikacji naszego rozszerzenia. Cały proces publikacji przedstawiony na gifie:

    Podsumowanie

    Będę szczery, spodziewałem się, że tworzenie pluginów na Figmę będzie miało więcej takie możliwości, jak udostępnienie użytkowników mających dostęp do otwartego projektu oraz włączanie funkcji plugin podczas otwierania projektu. Nie oznacza to jednak, że nie zmieni się to w przyszłości. Natomiast za dokumentację należy im się medal, bo wszystkie informacje były do znalezienia na stronie https://www.figma.com/plugin-docs/intro/.

    Jeśli masz jakiekolwiek pytania zapraszam do sekcji komentarzy. Kod stworzony podczas pisania artykułu znajduje się tutaj: https://github.com/korczynsk1/deadline.

    Jeśli będziesz chciał zobaczyć jak kod działa na żywym organizmie to sklonuj repozytorium, odpal npm start i zaimportuj manifest.json w sekcji Plugins i modyfikuj kod. 🙂

    Mateusz Korczyński
    Fullstack Developer orbitujący głównie wokół Javy i Reacta. Posiada 5-letnie doświadczenie komercyjne od mikroprzedsiębiorstwa po korporacje. Jednak swoje miejsce na ziemi znalazł w software housie. Miesiąc bez poznania nowego frameworka jest dla niego miesiącem straconym, więc stara się ciągle trzymać rękę na pulsie.

    Latest articles

    Automatyzacja z Pythonem – pobieranie kolekcji filmów z YouTube

    Automatyzacja czynności, które są czasochłonne, to według mnie największa zaleta nauki programowania. Często zadania wymagające od nas klikania, czekania i przepisywania captchy...

    Programowanie funkcyjne w JS

    Jeśli bierzesz udział w rozmowach rekrutacyjnych na stanowisko regular lub senior developera, to pewnie niejednokrotnie miałeś lub miałaś do czynienia z takimi...

    Własny plugin do Figmy w React.js

    Jeśli udało Ci się kiedykolwiek pracować z Figmą, istnieje spora szansa, że narzędzie przypadło Ci do gustu. Tak przynajmniej było w moim...

    Wykorzystanie Virtual DOM na przykładzie Reacta

    Virtual DOM to bardzo popularne rozwiązanie znane z Reacta. Co to dokładnie jest? W jaki sposób działa w Reactcie? O tym opowie...

    Leave a reply

    Please enter your comment!
    Please enter your name here

    Related articles

    X