AddToPlaylistDropdown.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <template>
  2. <div id="nav-dropdown">
  3. <div
  4. class="nav-dropdown-items"
  5. v-if="playlists.length > 0"
  6. v-click-outside="() => (this.$parent.showPlaylistDropdown = false)"
  7. >
  8. <!-- <a class="nav-item" id="nightmode-toggle">
  9. <span>Nightmode</span>
  10. <label class="switch">
  11. <input type="checkbox" checked />
  12. <span class="slider round"></span>
  13. </label>
  14. </a> -->
  15. <button
  16. class="nav-item"
  17. href="#"
  18. v-for="(playlist, index) in playlists"
  19. :key="index"
  20. @click.prevent="toggleSongInPlaylist(index, playlist._id)"
  21. :title="playlist.displayName"
  22. >
  23. <p class="control is-expanded checkbox-control">
  24. <input
  25. type="checkbox"
  26. :id="index"
  27. v-model="playlist.hasSong"
  28. />
  29. <label :for="index">
  30. <span></span>
  31. <p>{{ playlist.displayName }}</p>
  32. </label>
  33. </p>
  34. </button>
  35. </div>
  36. <p class="nav-dropdown-items" id="no-playlists" v-else>
  37. You haven't created any playlists.
  38. </p>
  39. </div>
  40. </template>
  41. <script>
  42. import Toast from "toasters";
  43. import io from "../../io";
  44. export default {
  45. props: {
  46. song: {
  47. type: Object,
  48. default: () => {}
  49. }
  50. },
  51. data() {
  52. return {
  53. playlists: []
  54. };
  55. },
  56. mounted() {
  57. io.getSocket(socket => {
  58. this.socket = socket;
  59. this.socket.emit("playlists.indexMyPlaylists", false, res => {
  60. if (res.status === "success") {
  61. this.playlists = res.data;
  62. this.checkIfPlaylistsHaveSong();
  63. }
  64. });
  65. this.socket.on("event:songs.next", () => {
  66. this.checkIfPlaylistsHaveSong();
  67. });
  68. this.socket.on("event:playlist.create", playlist => {
  69. this.playlists.push(playlist);
  70. });
  71. this.socket.on("event:playlist.delete", playlistId => {
  72. this.playlists.forEach((playlist, index) => {
  73. if (playlist._id === playlistId) {
  74. this.playlists.splice(index, 1);
  75. }
  76. });
  77. });
  78. this.socket.on("event:playlist.updateDisplayName", data => {
  79. this.playlists.forEach((playlist, index) => {
  80. if (playlist._id === data.playlistId) {
  81. this.playlists[index].displayName = data.displayName;
  82. }
  83. });
  84. });
  85. });
  86. },
  87. methods: {
  88. toggleSongInPlaylist(index, playlistId) {
  89. if (!this.playlists[index].hasSong) {
  90. this.socket.emit(
  91. "playlists.addSongToPlaylist",
  92. false,
  93. this.song.songId,
  94. playlistId,
  95. res => {
  96. new Toast({ content: res.message, timeout: 4000 });
  97. if (res.status === "success") {
  98. this.playlists[index].songs.push(this.song);
  99. this.playlists[index].hasSong = true;
  100. }
  101. }
  102. );
  103. } else {
  104. this.socket.emit(
  105. "playlists.removeSongFromPlaylist",
  106. this.song.songId,
  107. playlistId,
  108. res => {
  109. new Toast({ content: res.message, timeout: 4000 });
  110. if (res.status === "success") {
  111. this.playlists[index].songs.forEach(
  112. (song, songIndex) => {
  113. if (song.songId === this.song.songId)
  114. this.playlists[index].songs.splice(
  115. songIndex,
  116. 1
  117. );
  118. }
  119. );
  120. this.playlists[index].hasSong = false;
  121. }
  122. }
  123. );
  124. }
  125. },
  126. checkIfPlaylistsHaveSong() {
  127. this.playlists.forEach((playlist, index) => {
  128. let hasSong = false;
  129. for (let song = 0; song < playlist.songs.length; song += 1) {
  130. if (playlist.songs[song].songId === this.song.songId)
  131. hasSong = true;
  132. }
  133. this.$set(this.playlists[index], "hasSong", hasSong);
  134. });
  135. }
  136. }
  137. };
  138. </script>
  139. <style lang="scss" scoped>
  140. .night-mode {
  141. .nav-dropdown-items {
  142. background-color: var(--dark-grey-2);
  143. border: 0 !important;
  144. .nav-item {
  145. background-color: var(--dark-grey);
  146. &:focus {
  147. outline-color: var(--dark-grey);
  148. }
  149. p {
  150. color: var(--white);
  151. }
  152. .checkbox-control label span {
  153. background-color: var(--dark-grey-2);
  154. }
  155. }
  156. }
  157. }
  158. #nav-dropdown {
  159. z-index: 1;
  160. }
  161. #nav-dropdown-triangle {
  162. border-style: solid;
  163. border-width: 15px 15px 0 15px;
  164. border-color: var(--dark-grey-2) transparent transparent transparent;
  165. }
  166. .nav-dropdown-items {
  167. border: 1px solid var(--light-grey-3);
  168. box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
  169. background-color: var(--white);
  170. padding: 5px;
  171. border-radius: 5px;
  172. z-index: 1;
  173. .nav-item {
  174. width: 100%;
  175. justify-content: flex-start;
  176. border: 0;
  177. padding: 10px;
  178. font-size: 15.5px;
  179. height: 36px;
  180. background: var(--light-grey);
  181. border-radius: 5px;
  182. cursor: pointer;
  183. .checkbox-control {
  184. display: flex;
  185. align-items: center;
  186. margin-bottom: 0 !important;
  187. input {
  188. margin-right: 5px;
  189. }
  190. input[type="checkbox"] {
  191. opacity: 0;
  192. position: absolute;
  193. }
  194. label {
  195. display: flex;
  196. flex-direction: row;
  197. align-items: center;
  198. span {
  199. cursor: pointer;
  200. width: 24px;
  201. height: 24px;
  202. background-color: var(--white);
  203. display: inline-block;
  204. border: 1px solid var(--dark-grey-2);
  205. position: relative;
  206. border-radius: 3px;
  207. }
  208. p {
  209. margin-left: 10px;
  210. cursor: pointer;
  211. color: var(--black);
  212. }
  213. }
  214. input[type="checkbox"]:checked + label span::after {
  215. content: "";
  216. width: 18px;
  217. height: 18px;
  218. left: 2px;
  219. top: 2px;
  220. border-radius: 3px;
  221. background-color: var(--primary-color);
  222. position: absolute;
  223. }
  224. }
  225. &:focus {
  226. outline-color: var(--light-grey-3);
  227. }
  228. &:not(:last-of-type) {
  229. margin-bottom: 5px;
  230. }
  231. }
  232. }
  233. #nightmode-toggle {
  234. display: flex;
  235. justify-content: space-evenly;
  236. }
  237. /* CSS - Toggle Switch */
  238. .switch {
  239. position: relative;
  240. display: inline-block;
  241. width: 50px;
  242. height: 24px;
  243. input {
  244. opacity: 0;
  245. width: 0;
  246. height: 0;
  247. }
  248. }
  249. .slider {
  250. position: absolute;
  251. cursor: pointer;
  252. top: 0;
  253. left: 0;
  254. right: 0;
  255. bottom: 0;
  256. background-color: var(--light-grey-3);
  257. transition: 0.4s;
  258. border-radius: 34px;
  259. &:before {
  260. position: absolute;
  261. content: "";
  262. height: 16px;
  263. width: 16px;
  264. left: 4px;
  265. bottom: 4px;
  266. background-color: var(--white);
  267. transition: 0.4s;
  268. border-radius: 50%;
  269. }
  270. }
  271. input:checked + .slider {
  272. background-color: var(--primary-color);
  273. }
  274. input:focus + .slider {
  275. box-shadow: 0 0 1px var(--primary-color);
  276. }
  277. input:checked + .slider:before {
  278. transform: translateX(26px);
  279. }
  280. #no-playlists {
  281. padding: 10px;
  282. }
  283. </style>