LoginSignup
1
0

More than 1 year has passed since last update.

Next.jsでNotion APIを使った<ul>と<li>の取り扱い【notion-blog-nextjs】

Last updated at Posted at 2022-03-11

やること

  • notion-blog-nextjs を元にして
  • <li><ul> で囲み
  • Notionと表記を(だいたい)同期させる
  • …あっ、CSSは tailwind で書いていきます

notion-blog-nextjsを導入する

  • Githubに公開されている samuelkraft / notion-blog-nextjs
  • 好きなフォルダに clone して
  • 該当ディレクトリに cd ~ して
  • npm install してパッケージをインストールして
  • npm run dev で動かせます
  • あとは .env.local にいろいろ書けば自分のNotionのデータを参照させられます。

詳しくは以下リポジトリのREADMEを参考にしてください。

https://github.com/samuelkraft/notion-blog-nextjs

notion-blog-nextjsのしくみ

  • ビルドすると index.js が組み立てられる
  • Notionのページはそれぞれ [id].js を雛形に組み立てられる

むっちゃ簡単に言うとこんな感じです。
つまり、 [id].js をいじればいろいろ変えられるということですね。

idページを編集する

[id].jsのここをいじると、idページの見た目を変えることができます。
自由に書き加えていきましょう。

export default function Post({ page, blocks }) {
  if (!page || !blocks) {
    return <div />;
  }
  return (
    <div>
      <Head>
        <title>{page.properties.Name.title[0].plain_text}</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <article className={styles.container}>
        <h1 className={styles.name}>
          <Text text={page.properties.Name.title} />
        </h1>
        <section>
          {blocks.map((block) => (
            <Fragment key={block.id}>{renderBlock(block)}</Fragment>
          ))}
          <Link href="/">
            <a className={styles.back}>← Go home</a>
          </Link>
        </section>
      </article>
    </div>
  );
}

今回大事になってくるのがここになります。

{blocks.map((block) => (
  <Fragment key={block.id}>{renderBlock(block)}</Fragment>
))}

今の時点ではとてもシンプルですが、ここに以下の機能を足していきます。

  • リストの状況を見ながら <ul>~</ul> を挿入する。
  • Notion側の表記に応じて、リストの点を数字かポチか決める。

つまるところ目標は

Notionで書いたこれの再現。

https://i.gyazo.com/746e64a0ebc8a5fedff8948947df94c1.png

とりあえず想定していることとして

  • 複数個のリストの取扱い
  • 種類の違うリストの取扱い
  • リストをつなげるとどうなるのか
  • リストをネストさせているとどうなるのか
  • ちゃんと他の要素(今回はプレーンなテキストも表示できるか)

を見ていきます。

とりあえず結論

こう書けばいい感じの挙動になります。(投げやり)
ちなみに、ここから tailwindを使って装飾しています。

export function make_ul_li(list_blocks, now_block = null) {
    const listclass = "ml-5 p-5";

    var ul_class;
    switch (list_blocks[0].type) {
        case "numbered_list_item":
            ul_class = "list-decimal";
            break;
        case "bulleted_list_item":
            ul_class = "list-disc";
            break;
    }
    return (
        <>
            <ul key={list_blocks.key} className={`${ul_class} ${listclass}`}>
                {list_blocks.map((list_block) => renderBlock(list_block))}
            </ul>
        </>
    );
}

export function Pages_template({ blocks, pagename }) {
    var disc_list = [];
    var decimal_list = [];

    const TYPE_DECIMAL = "numbered_list_item";
    const TYPE_DISC = "bulleted_list_item";
		const c = "flex flex-col w-5/6 m-auto content-center";
    var output_code = "";

    return (
        <div className={c}>
            <Header pagename={pagename} />
            <h1 className="text-4xl font-bold pb-5">{pagename}</h1>
            <section>
                {blocks.map((block) => {
                    if (~block.type.indexOf("list")) {
                        if (decimal_list.length > 0) {
                            switch (block.type) {
                                case TYPE_DISC:
                                    output_code = make_ul_li(decimal_list);
                                    disc_list.push(block);
                                    decimal_list = [];
                                    return output_code;
                                case TYPE_DECIMAL:
                                    decimal_list.push(block);
                                    break;
                            }
                        } else if (disc_list.length > 0) {
                            switch (block.type) {
                                case TYPE_DISC:
                                    disc_list.push(block);
                                    break;
                                case TYPE_DECIMAL:
                                    output_code = make_ul_li(disc_list);
                                    decimal_list.push(block);
                                    disc_list = [];
                                    return output_code;
                            }
                        } else if (
                            decimal_list.length == 0 ||
                            disc_list.length == 0
                        ) {
                            switch (block.type) {
                                case TYPE_DECIMAL:
                                    decimal_list.push(block);
                                    break;

                                case TYPE_DISC:
                                    disc_list.push(block);
                                    break;
                            }
                        }
                    } else {
                        if (decimal_list.length > 0) {
                            output_code = make_ul_li(decimal_list, block);
                            decimal_list = [];
                            return output_code;
                        } else if (disc_list.length > 0) {
                            output_code = make_ul_li(disc_list, block);
                            disc_list = [];
                            return output_code;
                        } else if (
                            decimal_list.length == 0 &&
                            disc_list.length == 0
                        ) {
                            return renderBlock(block);
                        }
                    }
                })}
            </section>
            <Footer />
        </div>
    );
}

これをいい感じに [id].js に流し込んでやると、

https://i.gyazo.com/5c1b60c98d7e24c19aa15f7018c67209.png

こんな感じになります。

さしすせそ の部分や、 おおかかの間あたりのスペースが残念な結果になりましたが、
まあこう書くほうが悪いだろってことで無視していきます。

コード解説

分けて解説していきます。

Pages_templateについて

  • いろいろな変数や定数を設定していきます。
  • disc_listdecimal_listで、点のタイプを仕分けていきます。
  • constの類はいちいち変えるのがめんどくさい箇所に設定しています。
var disc_list = [];
var decimal_list = [];

const TYPE_DECIMAL = "numbered_list_item";
const TYPE_DISC = "bulleted_list_item";
const c = "flex flex-col w-5/6 m-auto content-center";
var output_code = "";
  • <Header ~/><Footer ~/> は他コンポーネントからimportしています。
    ちなみに中身は文字通りページのヘッダーやフッター、head部分の処理を担っています。
return (
        <div className={c}>
            <Header pagename={pagename} />
            <h1 className="text-4xl font-bold pb-5">{pagename}</h1>
            <section>
               ~
            </section>
            <Footer />
        </div>
    );
}

今回一番のキモです。

{blocks.map((block) => {
    if (~block.type.indexOf("list")) {
        if (decimal_list.length > 0) {
            switch (block.type) {
                case TYPE_DISC:
                    output_code = make_ul_li(decimal_list);
                    disc_list.push(block);
                    decimal_list = [];
                    return output_code;
                case TYPE_DECIMAL:
                    decimal_list.push(block);
                    break;
            }
        } else if (disc_list.length > 0) {
            switch (block.type) {
                case TYPE_DISC:
                    disc_list.push(block);
                    break;
                case TYPE_DECIMAL:
                    output_code = make_ul_li(disc_list);
                    decimal_list.push(block);
                    disc_list = [];
                    return output_code;
            }
        } else if (
            decimal_list.length == 0 ||
            disc_list.length == 0
        ) {
            switch (block.type) {
                case TYPE_DECIMAL:
                    decimal_list.push(block);
                    break;

                case TYPE_DISC:
                    disc_list.push(block);
                    break;
            }
        }
    } else {
        if (decimal_list.length > 0) {
            output_code = make_ul_li(decimal_list, block);
            decimal_list = [];
            return output_code;
        } else if (disc_list.length > 0) {
            output_code = make_ul_li(disc_list, block);
            disc_list = [];
            return output_code;
        } else if (
            decimal_list.length == 0 &&
            disc_list.length == 0
        ) {
            return renderBlock(block);
        }
    }
})}

日本語にすると、処理はだいたい以下のようになっています。

  • block.type”list”という文字列が入っているか?
    • 入ってます、それは間違いなく<li>に該当する要素です
      • disc_list、もしくは decimal_list に値が入っていますか?
        • 入っています
          • 現在値が入るのは同じリストですか?
            • 同じリストです、現在値をそのリストに追加して最初に戻ります。
            • 違うリストです
              • リストを <ul>にまとめて、リストは初期化します。
                違うリストに現在値を追加して最初に戻ります。
        • 入っていません
          • 現在値が入るのは同じリストですか?
            • 同じリストです、現在値をそのリストに追加して最初に戻ります。
            • 違うリストです
              • リストを <ul>にまとめて書き出します。
                リストは初期化します。
                違うリストに現在値を追加して最初に戻ります。
    • 入っていません、それは別の要素になります。
      • disc_list、もしくは decimal_list に値が入っていますか?
        • 入っています
          • リストを <ul>にまとめて書き出します。
            リストは初期化します。
            要素をそのまま書き出します。
            最初に戻ります。
        • 入っていません
          • 要素をそのまま書き出します。
            最初に戻ります。

で、 <ul>を書き出す際には以下の処理に通していきます。

export function make_ul_li(list_blocks) {
    const listclass = "ml-5 p-5";

    var ul_class;
    switch (list_blocks[0].type) {
        case "numbered_list_item":
            ul_class = "list-decimal";
            break;
        case "bulleted_list_item":
            ul_class = "list-disc";
            break;
    }
    return (
        <>
            <ul key={list_blocks.key} className={`${ul_class} ${listclass}`}>
                {list_blocks.map((list_block) => renderBlock(list_block))}
            </ul>
        </>
    );
}

以上です。
割と力技みたいな感じになったので、もっとスマートな書き方があればご教示ください。

詰まったところ

リストの個数はちゃんと .lengthつけてあげる

動かない

if (配列 > 0) 

動く

if (配列.length > 0) 

.mapは案外自由が効く

関数と考え方が似ているなと思いました。
仕組みさえわかればこっちのものですね。

1
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
1
0