{"version":3,"file":"vitepress-plugin-music.umd.cjs","sources":["../src/utils/audio.ts","../src/template.vue","../src/index.ts"],"sourcesContent":["/**\n * Volume fade-in or fade-out\n */\nlet animationFrame\nexport const audioVolumeFade = (audioEl: HTMLAudioElement, isFadeIn?: boolean) => new Promise((resolve, reject) => {\n  cancelAnimationFrame(animationFrame)\n\n  audioEl.volume = isFadeIn ? 0 : 1\n  \n  /**\n   * done: Volume Limit's Variable\n   * gap: ease-in-out's Variable\n   */\n  let done = false, gap = 30\n  const step = () => {\n    audioEl.volume = Math.min(Math.max(audioEl.volume + (isFadeIn ? gap : -gap) / 1000, 0), 1)\n    \n    done = audioEl.volume === 1 || audioEl.volume === 0\n\n    if (!done) {\n      gap = Math.max(gap - 1, 5)\n      animationFrame = requestAnimationFrame(step)\n    } else {\n      resolve(audioEl.volume)\n    }\n  }\n  animationFrame = requestAnimationFrame(step)\n})\n","<template>\n  <div class=\"vitepress-music\" :class=\"playClassName\">\n    <div class=\"vitepress-music__drawer\">\n      <div class=\"vitepress-music__drawer-header\">\n        <div>\n          <span class=\"iconfont icon-shouqi\" @click=\"handleShowList\"></span>\n          <span>Playlist</span>\n        </div>\n        <span>Total {{audioList?.length || 0}}</span>\n      </div>\n      <ul class=\"vitepress-music__drawer-content\">\n        <template v-for=\"(item, index) in audioList\">\n          <li\n            v-if=\"!item.hide\"\n            :key=\"item.name\" \n            :class=\"{\n              active: playInfo.currentIndex === index,\n              error: playInfo.errorIndexList.includes(index),\n              playing: isPlay && playInfo.currentIndex === index,\n            }\"\n            @click=\"handleChangePlayIndex(index)\"\n          >\n            <div>{{item.name}}</div>\n            <div>{{item.author}}</div>\n          </li>\n        </template>\n      </ul>\n    </div>\n    <div class=\"vitepress-music__trigger\">\n      <span class=\"iconfont icon-changpianji\" @click=\"handleChangePlayStatus\"></span>\n      <span class=\"iconfont icon-shangyishou\" @click=\"handleChangePlayIndex(playInfo.currentIndex - 1)\"></span>\n      <span class=\"iconfont icon-xiayishou\" @click=\"handleChangePlayIndex(playInfo.currentIndex + 1)\"></span>\n      <span class=\"iconfont icon-gedan\" @click=\"handleShowList\"></span>\n    </div>\n    <audio ref=\"audioRef\"\n      :src=\"currentSongInfo.file\"\n      controls=\"controls\"\n      preload=\"auto\"\n      @ended=\"handleChangePlayIndex(playInfo.currentIndex + 1)\"\n      @error=\"handleAudioError()\"\n    />\n  </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport {ref, computed, watch, nextTick} from 'vue'\nimport {audioVolumeFade} from './utils/audio'\nimport './static/iconfont/iconfont.css'\nimport type {Ref} from 'vue'\nimport type {SongInfo} from './type'\n\nconst props = defineProps<{\n  list: SongInfo[]\n}>()\n\nenum PlayStatus {\n  play,\n  pause,\n  stop,\n}\n\nconst playInfo = ref({\n  status: PlayStatus.stop,\n  showList: false,\n  currentIndex: 0,\n  errorIndexList: [],\n})\nconst audioList = computed(() => props.list.filter(i => !i.hide))\nconst isPlay = computed(() => playInfo.value.status === PlayStatus.play)\nconst isPause = computed(() => playInfo.value.status === PlayStatus.pause)\nconst isStop = computed(() => playInfo.value.status === PlayStatus.stop)\nconst playClassName = computed(() => ({\n  'is-play': isPlay.value,\n  'is-pause': isPause.value,\n  'is-stop': isStop.value,\n  'is-show-list': playInfo.value.showList,\n}))\nconst currentSongInfo = computed(() => audioList.value[playInfo.value.currentIndex])\n\nconst handleChangePlayStatus = () => {\n  playInfo.value.status = isPlay.value ? PlayStatus.pause : PlayStatus.play\n}\nconst handleShowList = () => {\n  playInfo.value.showList = !playInfo.value.showList\n}\n\nconst audioRef: Ref<HTMLAudioElement> = ref()\nconst handleChangePlayIndex = (newIndex: number) => {\n  if (newIndex === playInfo.value.currentIndex) return handleChangePlayStatus()\n  playInfo.value.status = PlayStatus.stop\n  playInfo.value.currentIndex = newIndex < 0 ? audioList.value.length - 1 : newIndex > audioList.value.length - 1 ? 0 : newIndex\n  nextTick(() => playInfo.value.status = PlayStatus.play)\n}\nconst handleAudioError = () => {\n  const {errorIndexList, currentIndex} = playInfo.value\n  if (!errorIndexList.includes(currentIndex)) errorIndexList.push(currentIndex)\n  if (errorIndexList.length < audioList.value.length) handleChangePlayIndex(currentIndex + 1)\n}\n\nwatch(() => playInfo.value.status, async (value) => {\n  switch (value) {\n    case PlayStatus.play:\n      audioRef.value.play()\n      audioVolumeFade(audioRef.value, true)\n      break\n    case PlayStatus.pause:\n      await audioVolumeFade(audioRef.value, false)\n      audioRef.value.pause()\n      break\n    case PlayStatus.stop:\n      await audioVolumeFade(audioRef.value, false)\n      audioRef.value.pause()\n      break\n  \n    default:\n      break\n  }\n})\n</script>\n\n<style lang=\"postcss\">\n@keyframes rotate {\n  0% {\n    transform: rotate(0deg);\n  }\n  50%{\n    transform: rotate(180deg);\n  }\n  100% {\n    transform: rotate(360deg);\n  }\n}\n@keyframes fade-in {\n  0% {\n    transform: translateY(100%);\n    opacity: 0;\n  }\n  100% {\n    transform: translateY(0);\n    opacity: 0.9;\n  }\n}\n.vitepress-music{\n  position: fixed;\n  bottom: 100px;\n  right: 0;\n  z-index: 100;\n  &__trigger{\n    width: 120px;\n    height: 30px;\n    border-radius: 15px 0 0 15px;\n    cursor: pointer;\n    background-color: var(--vp-c-bg-alt);\n    box-shadow: 0 1px 8px 0 rgba(0, 0, 0, .1), 0 3px 4px 0 rgba(0, 0, 0, .07), 0 3px 3px -2px rgba(0, 0, 0, .06);\n    transform: translateX(90px);\n    transition: 200ms transform;\n    display: flex;\n    align-items: center;\n    justify-content: space-around;\n    color: #8a8a8a;\n    &:hover{\n      transform: translateX(0);\n    }\n    span{\n      transition: 500ms;\n      font-size: 20px;\n      &:hover{\n        color: var(--vp-c-brand);\n      }\n      &:nth-of-type(1){\n        animation: rotate 2s linear infinite paused;\n      }\n    }\n  }\n  &__drawer{\n    position: absolute;\n    top: -300px;\n    right: 0;\n    width: 200px;\n    height: 300px;\n    background-color: var(--vp-c-bg-alt);\n    box-shadow: 0 1px 8px 0 rgba(0, 0, 0, .1), 0 3px 4px 0 rgba(0, 0, 0, .07), 0 3px 3px -2px rgba(0, 0, 0, .06);\n    font-size: 12px;\n    padding: 12px 10px;\n    transform-origin: bottom right;\n    transform: scale(0);\n    transition: 200ms transform;\n    border-radius: 3px 0 0 3px;\n    display: flex;\n    flex-direction: column;\n    &-header{\n      margin-bottom: 5px;\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      color: var(--vp-c-text-2);\n      div{\n        display: flex;\n        justify-content: space-between;\n        align-items: center;\n        .icon-shouqi{\n          font-size: 10px;\n          margin-right: 5px;\n          cursor: pointer;\n          transition: 500ms;\n          &:hover{\n            color: var(--vp-c-brand);\n          }\n        }\n      }\n      /* display: none; */\n    }\n    &-content{\n      overflow: auto;\n      li{\n        position: relative;\n        display: flex;\n        cursor: pointer;\n        padding: 2px 0;\n        color: var(--vp-c-text-1);\n        overflow: hidden;\n        >*{\n          flex-wrap: nowrap;\n          flex-shrink: 0;\n          overflow: hidden;\n          white-space: nowrap;\n          text-overflow: ellipsis;\n        }\n        :nth-child(1){\n          flex-grow: 1;\n          width: 0;\n          padding-right: 5px;\n        }\n        :nth-child(2){\n          width: 50px;\n          text-align: right;\n        }\n        &.active{\n          color: var(--vp-c-brand);\n        }\n        &.error{\n          color: var(--vp-c-red-dimm-1);\n          text-decoration-line: line-through;\n        }\n        &:hover{\n          &:after{\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background-color: var(--vp-c-bg-alt);\n            color: var(--vp-c-brand);\n            text-align: center;\n            font-style: oblique;\n            animation: fade-in 500ms forwards;\n          }\n          &.playing:after{\n            content: 'click to pause';\n          }\n          &.error:after{\n            content: 'click to re-request';\n            color: var(--vp-c-red-dimm-1);\n          }\n          &:not(.playing, .error):after{\n            content: 'click to play';\n          }\n        }\n      }\n    }\n  }\n  &.is-play{\n    .vitepress-music__trigger{\n      span{\n        &:nth-of-type(1){\n          color: var(--vp-c-brand);\n          animation-play-state: running;\n        }\n      }\n      \n    }\n  }\n  &.is-pause{\n    \n  }\n  &.is-stop{\n    \n  }\n  &.is-show-list{\n    .vitepress-music__trigger{\n      span{\n        &:nth-of-type(4){\n          color: var(--vp-c-brand);\n        }\n      }\n    }\n    .vitepress-music__drawer{\n      transform: scale(1);\n    }\n  }\n  audio{\n    display: none;\n  }\n}\n</style>","import {render, createVNode} from 'vue'\nimport MusicCom from './template.vue'\nimport type {SongInfo} from './type'\n\nconst vitepressMusic = (list: SongInfo[] = []) => {\n  if (typeof window === 'undefined') return\n\n  const body = document.getElementsByTagName('body')[0]\n  const vnode = createVNode(MusicCom, {\n    list,\n  })\n  const boxEl = document.createElement('div')\n  render(vnode, boxEl)\n  document.body.appendChild(boxEl)\n}\n\nexport default vitepressMusic\n"],"names":["animationFrame","audioVolumeFade","audioEl","isFadeIn","resolve","reject","done","gap","step","playInfo","ref","audioList","computed","props","i","isPlay","isPause","isStop","playClassName","currentSongInfo","handleChangePlayStatus","handleShowList","audioRef","handleChangePlayIndex","newIndex","nextTick","handleAudioError","errorIndexList","currentIndex","watch","value","list","vnode","createVNode","MusicCom","boxEl","render"],"mappings":";+BAGA,IAAIA,EACS,MAAAC,EAAkB,CAACC,EAA2BC,IAAuB,IAAI,QAAQ,CAACC,EAASC,IAAW,CACjH,qBAAqBL,CAAc,EAE3BE,EAAA,OAASC,EAAW,EAAI,EAM5B,IAAAG,EAAO,GAAOC,EAAM,GACxB,MAAMC,EAAO,IAAM,CACjBN,EAAQ,OAAS,KAAK,IAAI,KAAK,IAAIA,EAAQ,QAAUC,EAAWI,EAAM,CAACA,GAAO,IAAM,CAAC,EAAG,CAAC,EAEzFD,EAAOJ,EAAQ,SAAW,GAAKA,EAAQ,SAAW,EAE7CI,EAIHF,EAAQF,EAAQ,MAAM,GAHtBK,EAAM,KAAK,IAAIA,EAAM,EAAG,CAAC,EACzBP,EAAiB,sBAAsBQ,CAAI,EAG7C,EAEFR,EAAiB,sBAAsBQ,CAAI,CAC7C,CAAC,6TCkCKC,EAAWC,EAAAA,IAAI,CACnB,OAAQ,EACR,SAAU,GACV,aAAc,EACd,eAAgB,CAAC,CAAA,CAClB,EACKC,EAAYC,EAAAA,SAAS,IAAMC,EAAM,KAAK,OAAYC,GAAA,CAACA,EAAE,IAAI,CAAC,EAC1DC,EAASH,EAAAA,SAAS,IAAMH,EAAS,MAAM,SAAW,GAClDO,EAAUJ,EAAAA,SAAS,IAAMH,EAAS,MAAM,SAAW,GACnDQ,EAASL,EAAAA,SAAS,IAAMH,EAAS,MAAM,SAAW,GAClDS,EAAgBN,EAAAA,SAAS,KAAO,CACpC,UAAWG,EAAO,MAClB,WAAYC,EAAQ,MACpB,UAAWC,EAAO,MAClB,eAAgBR,EAAS,MAAM,QAC/B,EAAA,EACIU,EAAkBP,EAAS,SAAA,IAAMD,EAAU,MAAMF,EAAS,MAAM,aAAa,EAE7EW,EAAyB,IAAM,CACnCX,EAAS,MAAM,OAASM,EAAO,MAAQ,EAAmB,CAAA,EAEtDM,EAAiB,IAAM,CAC3BZ,EAAS,MAAM,SAAW,CAACA,EAAS,MAAM,QAAA,EAGtCa,EAAkCZ,EAAAA,MAClCa,EAAyBC,GAAqB,CAC9C,GAAAA,IAAaf,EAAS,MAAM,aAAc,OAAOW,EAAuB,EAC5EX,EAAS,MAAM,OAAS,EACxBA,EAAS,MAAM,aAAee,EAAW,EAAIb,EAAU,MAAM,OAAS,EAAIa,EAAWb,EAAU,MAAM,OAAS,EAAI,EAAIa,EACtHC,EAAAA,SAAS,IAAMhB,EAAS,MAAM,OAAS,CAAe,CAAA,EAElDiB,EAAmB,IAAM,CAC7B,KAAM,CAAC,eAAAC,EAAgB,aAAAC,GAAgBnB,EAAS,MAC3CkB,EAAe,SAASC,CAAY,GAAGD,EAAe,KAAKC,CAAY,EACxED,EAAe,OAAShB,EAAU,MAAM,QAAQY,EAAsBK,EAAe,CAAC,CAAA,EAG5FC,OAAAA,EAAAA,MAAM,IAAMpB,EAAS,MAAM,OAAQ,MAAOqB,GAAU,CAClD,OAAQA,EAAO,CACb,IAAK,GACHR,EAAS,MAAM,OACCrB,EAAAqB,EAAS,MAAO,EAAI,EACpC,MACF,IAAK,GACG,MAAArB,EAAgBqB,EAAS,MAAO,EAAK,EAC3CA,EAAS,MAAM,QACf,MACF,IAAK,GACG,MAAArB,EAAgBqB,EAAS,MAAO,EAAK,EAC3CA,EAAS,MAAM,QACf,KAIJ,CAAA,CACD,kjDCjHsB,CAACS,EAAmB,KAAO,CAChD,GAAI,OAAO,OAAW,IAAa,OAEtB,SAAS,qBAAqB,MAAM,EAAE,GAC7C,MAAAC,EAAQC,cAAYC,EAAU,CAClC,KAAAH,CAAA,CACD,EACKI,EAAQ,SAAS,cAAc,KAAK,EAC1CC,SAAOJ,EAAOG,CAAK,EACV,SAAA,KAAK,YAAYA,CAAK,CACjC"}