142 lines
3.4 KiB

  1. <template>
  2. <section class="flex flex-col items-center min-h-screen">
  3. <loader v-if="isLoading" class="fixed z-50 backdrop-blur-sm p-4 rounded-full top-1/2" />
  4. <div class="container w-full p-6 max-w-6xl grow flex flex-col items-center">
  5. <nav
  6. class="w-full flex bg-slate-800 font-semibold rounded-full backdrop-blur sticky top-0 justify-center items-center hover:ring">
  7. <input ref="searchInput" v-model="terms" autofocus placeholder="search" type="search"
  8. class="rounded-full text-xl w-full text-center text-white p-4 bg-transparent focus-visible:border-none"
  9. @keypress.enter="pressEnter()">
  10. <b v-if="films && films.length"
  11. class="px-4 py-2 absolute right-2 block bg-green-400 text-slate-800 rounded-full">
  12. {{ films.length }}
  13. </b>
  14. </nav>
  15. <main class="flex max-w-screen-2xl justify-center flex-wrap m-6">
  16. <NuxtLink v-for="(film, index) in films" :key="index" :href="film.title" :to="'/details/' + film.id"
  17. class="hover:bg-slate-200 w-full flex flex-col bg-emerald-800 rounded-lg w-60 m-4">
  18. <img @error="setPlaceholder" :src="config.public.IMG_BASE_URL + film.poster_path" :alt="film.title"
  19. class="w-full rounded-t-lg">
  20. <span class="p-4">
  21. <h2 class="text-xl font-bold">
  22. {{ film.title }}
  23. </h2>
  24. <span v-if="film.release_date">
  25. {{ useDateFormat(film.release_date, 'D MMM YYYY') }}
  26. </span>
  27. {{ formatPercentage(film.vote_average) }}%
  28. </span>
  29. </NuxtLink>
  30. </main>
  31. </div>
  32. </section>
  33. </template>
  34. <script setup lang="ts">
  35. import { useDateFormat } from '@vueuse/core'
  36. const config = useRuntimeConfig()
  37. const terms = ref('')
  38. const films = ref([])
  39. const page = ref(1)
  40. const isLoading = ref(true)
  41. const { onScrollEnd } = useScrollEnd()
  42. const search = async () => {
  43. isLoading.value = true
  44. if (terms.value !== '') {
  45. const { data } = await useFetch(`/api/search/${terms.value}-${page.value}`)
  46. console.log(data)
  47. films.value.push(...data.value)
  48. } else {
  49. const { data } = await useFetch(`/api/explore/${page.value}`)
  50. films.value.push(...data.value)
  51. }
  52. isLoading.value = false
  53. }
  54. const pressEnter = () => {
  55. page.value = 1
  56. films.value = []
  57. search()
  58. }
  59. const fromPercentToDegree = (percent) => {
  60. let deg = Math.round((percent / 10) * 180)
  61. console.log(deg)
  62. return [`rotate-[calc(${deg}deg-45deg)]`]
  63. }
  64. const formatPercentage = (percent) => {
  65. return Math.round(percent * 10)
  66. }
  67. const setPlaceholder = (event) => {
  68. event.target.src = 'https://via.placeholder.com/200x300'
  69. }
  70. onScrollEnd(() => {
  71. page.value += 1
  72. search()
  73. })
  74. onMounted(() => {
  75. nextTick(() => {
  76. search()
  77. })
  78. })
  79. </script>
  80. <style>
  81. .title b {
  82. @apply text-green-400 capitalize;
  83. }
  84. .icon {
  85. @apply w-4 h-4 mx-2;
  86. }
  87. .icon-container {
  88. color: #8094ae;
  89. @apply flex items-center
  90. }
  91. .icon-container .icon {
  92. color: rgb(109, 109, 109);
  93. }
  94. .icon-size p {
  95. color: rgb(54, 150, 75);
  96. }
  97. .icon-seed p {
  98. color: rgb(12, 108, 233);
  99. }
  100. .loader>div {
  101. animation: loader 2.4s cubic-bezier(0, 0.2, 0.8, 1) infinite;
  102. }
  103. @keyframes loader {
  104. 0%,
  105. 100% {
  106. animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5);
  107. }
  108. 0% {
  109. transform: rotateY(0deg);
  110. }
  111. 50% {
  112. transform: rotateY(1800deg);
  113. animation-timing-function: cubic-bezier(0, 0.5, 0.5, 1);
  114. }
  115. 100% {
  116. transform: rotateY(3600deg);
  117. }
  118. }
  119. </style>