0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Stencil.jsを使って、インタラクティブなGROWIプラグインを作る

Posted at

オープンソースのWikiであるGROWIにはプラグイン機能が用意されています。自社のデータを表示したり、表示をカスタマイズするのに利用できます。

今回は、インタラクティブな操作が可能になるGROWIプラグインを作ってみました。テンプレートも用意したので、手軽にGROWIプラグイン開発をはじめられます。

GROWIプラグインの難点

これまで、幾つかのGROWIプラグインを開発してきましたが、若干の不便さがありました。それは、Reactコンポーネントで表示処理を実装できるものの、useStateやasync/awaitがそのままでは実装できないことです。

ネットワークでデータを取得して表示に利用するというのは、 react-async を使えば解決できますが、あらかじめデータを取得する必要があるなど若干の不便さは残ります。

特に、ボタンを押したタイミングで何かを実行したり、ユーザーの入力を受け付けるようなGROWIプラグインが実現できないのは不便さがありました。

Stencil.jsを使う!

GROWI自体はReactを作られており、全体がReactアプリとしてマウントされています。その中で別なReactコンポーネントを作り、インタラクティブな機能を実現できません。

そこで、解決策として考えたのがStencil.jsです。Stencil.jsは、Web Componentsを作るためのフレームワークで、できあがったWeb ComponentsはReactやVue.js、Angularなどでも利用できます。そして、Stencil.jsのWeb Componentsの中では、独自にインタラクティブなUIであったり、画面への表示反映などが実現できます。

ということで、Stencil.jsをGROWIプラグインの中で使ってみました。

できたもの

これはデモですが、GROWIでボタンを表示しています。そして、そのボタンを押すとカウントアップします。ネットワーク処理ももちろん使えるので、データを保存したり、逆に呼び出すことも可能です。

FireShot Capture 375 - Stencil - GROWI - localhost.jpg

作り方

テンプレートはgoofmint/growi-plugin-script-template-stencil: GROWI script plugin template with stencilに作成しています。このプラグインでは <growi-component /> というタグを定義しています。

手順としては、まずStencil.jsでWeb Componentsを作成します。

Web Componentsの作成

growi-component/src 以下を編集します。 growi-component/src/components/my-component/growi-component.tsx を見ると、だいたい内容が分かるかと思います。

render メソッドでレンダリングを行い、変更される変数は @State で指定します。イベントは onClick などを使います。

import {
  Component, Prop, State, h,
} from '@stencil/core';

// import { format } from '../../utils/utils';

@Component({
  tag: 'growi-component',
  styleUrl: 'growi-component.css',
  shadow: true,
})
export class GrowiComponent {

  /**
   * The name
   */
  @Prop() name: string;

  /**
   * The parametar1
   */
  @Prop() params1: string;

  /**
   * The parametar2
   */
  @Prop() params2: string;

  /**
   * The count
   */
  @State() count = 0;

  private getText = (): string => {
    return `${this.name}. params1: ${this.params1} params2: ${this.params2}`;
  };

  onClick = (): void => {
    this.count += 1;
  };

  render(): JSX.Element {
    return (<div>Hello, World! I'm {this.getText()}! Count: {this.count}
      <button onClick={this.onClick}>Click me</button>
    </div>);
  }
}

Web Componentsのビルド

ビルドは pnpm run build でOKです。開発中は pnpm run build --watch しておくと便利です。

Web Componentsの読み込み

GROWIでは、 src/Hello.tsx にてWeb Componentsを読み込んでいます。

import '../growi-component/dist/components/growi-component';

読み込んでしまえば、後はレンダリングする際に <growi-component /> というタグを使うだけです。

export const helloGROWI = (Tag: React.FunctionComponent<any>): React.FunctionComponent<any> => {
  return ({ children, ...props }) => {
    try {
      const { stencil, params1, params2 } = JSON.parse(props.title || '{}');
      if (stencil) {
        return (
          <growi-component
            name={children}
            params-1={params1}
            params-2={params2}>
          </growi-component>
        );
      }
    }
    catch (err) {
      // console.error(err);
    }
    // Return the original component if an error occurs
    return (
      <Tag {...props}>{children}</Tag>
    );
  };
};

GROWIページ内では、 :plugin[label]{params1=test2 params2=test2} のように指定します。これをRemark Directiveとして処理する際に、 a タグに置き換えています。一緒に stencil = true も追加しておくことで、表示する際にプラグインなのかどうかを判別できます(上のコードへの処理分けに使っています)。

export const remarkPlugin: Plugin = () => {
  return (tree: Node) => {
    visit(tree, (node: Node) => {
      const n = node as unknown as GrowiNode;
      if (n.name !== 'plugin') return;
      const data = n.data || (n.data = {});
      // Render your component
      const { value } = n.children[0];
      data.hName = 'a'; // Tag name
      data.hChildren = [{ type: 'text', value }]; // Children
      // Set properties
      data.hProperties = {
        href: 'https://example.com/',
        title: JSON.stringify({ ...n.attributes, ...{ stencil: true } }), // Pass to attributes to the component
      };
    });
  };
};

Stencil.js + GROWIプラグインの可能性について

Stencil.jsを使うことで、以下のようなことが可能になると考えています。

  • CSSにTailwindを使う
  • RSSを取り込んで表示する際に、ついでにページを作る。これでRSSの内容をGROWIの検索対象にする
  • モーダルを表示して、データをメンテナンスする
  • 独自のBaaS(Supabaseなど)と組み合わせて、掲示板などを実装する
  • Backlog APIなどからデータを取得するだけでなく、データメンテナンスも行う

実体はWeb Componentsなので、GROWIに限らず利用できますが、GROWIの中に組み込んで使えるのが利点ではないでしょうか。

GROWIコミュニティについて

プラグインの使い方や要望などがあれば、ぜひGROWIコミュニティにお寄せください。実現できそうなものがあれば、なるべく対応します。他にもヘルプチャンネルなどもありますので、ぜひ参加してください!

GROWI Slackへの参加はこちらから

まとめ

GROWIプラグインを使うと、表示を自由に拡張できます。足りない機能があれば、どんどん追加できます。ぜひ、自分のWikiをカスタマイズしましょう。

OSS開発wikiツールのGROWI | 快適な情報共有を、全ての人へ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?