👶

[headlessUI]Menuを閉じる方法

2023/04/17に公開

始めに

本記事は特定環境でbuildした際に著しくパフォーマンスが落ちるか、build後の静的解析でエラーが発生する可能性があります。
ご注意ください。

概要

  • OSSコントリビュートの一環で、headlessUIで構築しているものの挙動を変えたいというissueがあったので対応してみた。
  • 課題としては、ミートボールメニュー中にディスクロージャーが存在しているが、ディスクロージャー中のメニューをクリックしても親のミートボールメニューが閉じてくれないので、閉じるように変更したい。
  • Nuxtで構築されていたが、今回の件には影響なし。

結論

  • Menuコンポーネント下で、MenuButtonを利用すればクリック時にMenuを閉じる挙動になる。
  • どうやるかなあ...とか考えてドキュメント眺めてたら答え書いてた。

修正後

ざっくりこんな感じ。

<template>
  <Menu as="div" class="relative inline-block text-left">
    <div>
      <MenuButton
        class="justify-center select-none rounded-md text-light-text dark:text-dark-text active:text-light-cta-orange active:dark:text-dark-cta-orange focus-brand"
      >
        <div>
          <Icon name="bi:three-dots-vertical" size="1.5em" />
        </div>
      </MenuButton>
    </div>

    <transition
      enter-active-class="transition duration-100 ease-out"
      enter-from-class="opacity-0 transform scale-95"
      enter-to-class="opacity-100 transform scale-100"
      leave-active-class="transition duration-75 ease-in"
      leave-from-class="opacity-100 transform scale-100"
      leave-to-class="opacity-0 transform scale-95"
    >
      <MenuItems
        class="absolute right-0 mt-2 border shadow-lg origin-top-right rounded-md bg-light-content dark:bg-dark-content ring-1 ring-black ring-opacity-5 focus:outline-none border-light-text dark:border-dark-text overflow-clip"
      >
        <div class="w-full mx-auto grid divide-y grid-row-2">
          <Disclosure v-slot="{ open }">
            <DisclosureButton
              :class="[
                open
                  ? 'bg-light-cta-orange dark:bg-dark-cta-orange text-light-content dark:text-dark-content'
                  : 'text-light-text dark:text-dark-text hover:bg-light-highlight dark:hover:bg-dark-highlight',
                'flex w-full items-center pr-6',
              ]"
            >
              <div
                class="relative z-0 flex items-center w-full px-3 py-2 text-sm font-medium text-left space-x-2"
              >
                <Icon
                  v-if="$colorMode.preference == 'system'"
                  name="bi:circle-half"
                />
                <Icon v-if="$colorMode.preference == 'light'" name="bi:sun" />
                <Icon
                  v-else-if="$colorMode.preference == 'dark'"
                  name="bi:moon"
                />
                <p>{{ $t("theme") }}</p>
              </div>
              <Icon
                name="bi:chevron-down"
                :class="
                  open
                    ? 'absolute right-0 mr-2 rotate-180 transform'
                    : 'absolute right-0 mr-2'
                "
              />
            </DisclosureButton>
            <DisclosurePanel class="px-3 py-2">
              <MenuButton
                class="flex items-center w-full px-2 py-2 text-sm rounded-md group text-light-text dark:text-dark-text hover:bg-light-highlight dark:hover:bg-dark-highlight"
                @click="$colorMode.preference = 'system'"
              >
                <Icon name="bi:circle-half" size="1em" />
                <p class="px-1">System</p>
              </MenuButton>
              <MenuButton
                class="flex items-center w-full px-2 py-2 text-sm rounded-md group text-light-text dark:text-dark-text hover:bg-light-highlight dark:hover:bg-dark-highlight"
                @click="$colorMode.preference = 'light'"
              >
                <Icon name="bi:sun" size="1em" />
                <p class="px-1">Light</p>
              </MenuButton>
              <MenuButton
                class="flex items-center w-full px-2 py-2 text-sm rounded-md group text-light-text dark:text-dark-text hover:bg-light-highlight dark:hover:bg-dark-highlight"
                @click="$colorMode.preference = 'dark'"
              >
                <Icon name="bi:moon" size="1em" />
                <p class="px-1">Dark</p>
              </MenuButton>
            </DisclosurePanel>
          </Disclosure>
        </div>
      </MenuItems>
    </transition>
  </Menu>
</template>

実装検討中のメモ

  • Disclosureというコンポーネントが使われていた。関連コンポーネントのDisclosureButtonは、一番近いラップされたコンポーネントの状態を変更する。(DisclosurePannelでラップしていたらそれを変更するっぽい)
    • これで解決できそうかなと意気揚々と実装したが、最上位のMenuの状態遷移をさせるには少し機能不足だった。
  • ボタンクリックでMenuの状態を変更することも考えたが、状態管理が増えて煩雑になりそうだった。
    • UIライブラリだけで完結できないかを模索

最後に

OSSで色んな人とやりとりするの楽しいからおすすめです(´・ω・`)

Discussion