🌳

ElmでFigmaプラグインを作る

2020/09/18に公開

サンプル

Elm で Figma プラグインを作るのがなかなか楽しかったのでサンプルを用意しました。

本記事ではリポジトリのサンプルを元に説明していきます。

準備

まずプロジェクトを作成しインストールを行います。(私はテンプレートとして github リポジトリを利用するときに degit をつかっています)

npx degit TheSacredLipton/figma-elm app
cd app
npm install

インストールが完了しましたら npm start で開発を始めることができます。

npm start

プラグインの追加

プラグインの開発にはデスクトップ版の Figma が必要になりますのでブラウザ版を使っている方は先にインストールしておいてください。

準備ができましたら、Create new plugin の隣の+をクリックします。

「Click to chose a manifest.json file」をクリックしエクスプローラーを開き、プロジェクトルートにある manifest.json を指定します。

figma でプロジェクトを開き「Plugins」->「Development」->「プラグイン名」で実行できます。

「-」でカウントを減らし、「+」でカウントを増やし「CreateRectangle」で図形を作成します。

うまくいったら以下のように図形がカウントの数だけ追加されます。

コード

今回 elm のサンプルを作るにあたりこちらのサンプルを参考にしました。
https://github.com/figma/plugin-samples/tree/master/webpack

私は elm で port を使う際 JS を使う派ですが、Figma のプラグイン開発では TS が推奨されているみたいですので今回 Elm と Typescript を組み合わせてサンプルを作成しております。
基本的にはこんな感じでやり取りをさせます。

今回のサンプルではこの方向に情報を渡していきます。

Ui.elm

Ui.elm ではプラグイン実行時に表示されるウィンドウの UI 構築をメインに、Typescript 側への命令を port で記述していきます。

port module Ui exposing (..)

...

port create : Int -> Cmd msg

Model ではカウント用に Int を指定しておきます。

type alias Model =
    Int


init : () -> ( Model, Cmd msg )
init _ =
    ( 1
    , Cmd.none
    )

update では elm のカウンターのサンプルの記述に加えて port 用に Create を追加しています。Create が実行されるとカウントが port で渡されます。

type Msg
    = Create
    | Increment
    | Decrement


update : Msg -> Model -> ( Model, Cmd msg )
update msg model =
    case msg of
        Create ->
            ( model, create model )

        Increment ->
            ( model + 1, Cmd.none )

        Decrement ->
            ( if model > 1 then
                model - 1

              else
                model
            , Cmd.none
            )

View では今回 elm-ui を使っています。カウンターのサンプルに加え、Create Rectangleボタンを追加しています。このボタンを押すことで update の Create が実行され port でカウントが渡される仕組みになっています。
elm-ui はいいぞ。

view : Model -> Html Msg
view model =
    Element.layout [ width fill, height fill ] <|
        column [ width fill, height fill ]
            [ row [ height fill, centerX, spacing 10 ]
                [ Input.button [ padding 10 ] { onPress = Just Decrement, label = text "-" }
                , text <| String.fromInt model
                , Input.button [ padding 10 ] { onPress = Just Increment, label = text "+" }
                ]
            , Input.button [ padding 10, centerX ] { onPress = Just Create, label = text "Create Rectangle" }
            ]

ui.ts

公式で型定義ファイルが用意されているので楽に開発できます。

今回は elm からポートで受け取った count を parent.postMessage で code.ts に渡しています。

import { Elm } from './Ui.elm'

const app = Elm.Ui.init({
  node: document.getElementById('main')
})

app.ports.create.subscribe((data) => {
  parent.postMessage({ pluginMessage: { type: 'create-rectangles', count: data } }, '*')
})

code.ts

code.ts はほぼサンプルコードを使っています。
ui.ts からカウントを受け取りその数だけ rect を作成する感じのコードです。

figma.showUI('<div id="main"></div>' + __html__)
figma.ui.resize(300, 300)

figma.ui.onmessage = msg => {
  if (msg.type === 'create-rectangles') {
    const nodes = []

    for (let i = 0; i < msg.count; i++) {
      const rect = figma.createRectangle()
      rect.x = i * 150
      rect.x = figma.viewport.center.x + i * 150
      rect.y = figma.viewport.center.y
      rect.fills = [{ type: 'SOLID', color: { r: 1, g: 0.5, b: 0 } }]
      figma.currentPage.appendChild(rect)
      nodes.push(rect)
    }

    figma.currentPage.selection = nodes
    figma.viewport.scrollAndZoomIntoView(nodes)
  }

  figma.closePlugin()
}

Figmaプラグイン開発のコツ

  • 現状ホットリロードさせる機能は無いはずです。Ctrl + Alt + p で最後に実行したプラグインを実行できるのでこちらのショートカットキーを覚えておくと便利です。

  • 「Plugins」->「Development」->「Open Console」でコンソールを開くこともできます。

  • manifest.json を変更したとき、一部の機能では一度拡張機能を削除してもう一度マニフェストファイルを読み込ませなければ正しく反映されないときがありますので注意してください。

  • 詳しいことはAPIリファレンスに全部乗っているので眺めておいてください。

  • マニフェストファイルに関しては公式ドキュメント が参考になります。

  • elm-ui の explain Debug.todo を使うと UI 構築が楽になります。elm-ui はいいぞ。

Discussion