More blurriness, and stylish
This commit is contained in:
11
app.vue
Normal file
11
app.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<template>
|
||||||
|
<div class="relative flex flex-col min-h-svh">
|
||||||
|
<header class="w-full sticky top-0 z-50">
|
||||||
|
<Navbar />
|
||||||
|
</header>
|
||||||
|
<main class="flex flex-1 flex-col">
|
||||||
|
<NuxtPage />
|
||||||
|
</main>
|
||||||
|
<Toaster />
|
||||||
|
</div>
|
||||||
|
</template>
|
3
assets/css/main.css
Normal file
3
assets/css/main.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.dark img {
|
||||||
|
filter: brightness(1.1) contrast(0.9);
|
||||||
|
}
|
14
components/ui/aspect-ratio/AspectRatio.vue
Normal file
14
components/ui/aspect-ratio/AspectRatio.vue
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { AspectRatio, type AspectRatioProps } from 'reka-ui'
|
||||||
|
|
||||||
|
const props = defineProps<AspectRatioProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AspectRatio
|
||||||
|
data-slot="aspect-ratio"
|
||||||
|
v-bind="props"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</AspectRatio>
|
||||||
|
</template>
|
1
components/ui/aspect-ratio/index.ts
Normal file
1
components/ui/aspect-ratio/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as AspectRatio } from './AspectRatio.vue'
|
@ -1,32 +1,34 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Icon } from '@iconify/vue'
|
import { Icon } from '@iconify/vue'
|
||||||
|
|
||||||
const colorMode = useColorMode()
|
const colorMode = useColorMode()
|
||||||
|
|
||||||
function toggleColorMode(): void {
|
function toggleColorMode(): void {
|
||||||
const currentColorMode = colorMode.value
|
const currentColorMode = colorMode.value
|
||||||
if (currentColorMode === 'light') {
|
if (currentColorMode === 'light') {
|
||||||
colorMode.preference = 'dark'
|
colorMode.preference = 'dark'
|
||||||
}
|
}
|
||||||
if (currentColorMode === 'dark') {
|
if (currentColorMode === 'dark') {
|
||||||
colorMode.preference = 'light'
|
colorMode.preference = 'light'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="absolute w-screen flex justify-around items-center p-2 border-b-2">
|
<div class="flex justify-around items-center p-2 border-b-2 backdrop-blur">
|
||||||
<div class="self-start">
|
<div class="self-start">
|
||||||
<h3 class="scroll-m-20 text-2xl font-semibold tracking-tight select-none">
|
<h3 class="scroll-m-20 text-2xl font-semibold tracking-tight select-none">
|
||||||
Anyame
|
Anyame
|
||||||
</h3>
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="self-end">
|
||||||
|
<Button variant="outline" @click="toggleColorMode()">
|
||||||
|
<Icon icon="radix-icons:moon"
|
||||||
|
class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||||
|
<Icon icon="radix-icons:sun"
|
||||||
|
class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||||
|
<span class="sr-only">Toggle theme</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="self-end">
|
</template>
|
||||||
<Button variant="outline" @click="toggleColorMode()">
|
|
||||||
<Icon icon="radix-icons:moon" class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
|
||||||
<Icon icon="radix-icons:sun" class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
|
||||||
<span class="sr-only">Toggle theme</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
@ -8,7 +8,10 @@ export default defineNuxtConfig({
|
|||||||
colorMode: {
|
colorMode: {
|
||||||
classSuffix: ''
|
classSuffix: ''
|
||||||
},
|
},
|
||||||
css: ['~/assets/css/tailwind.css'],
|
css: [
|
||||||
|
'~/assets/css/tailwind.css',
|
||||||
|
'~/assets/css/main.css',
|
||||||
|
],
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [
|
plugins: [
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
|
123
pages/index.vue
123
pages/index.vue
@ -3,85 +3,78 @@
|
|||||||
import kodikImage from "assets/img/kodik.png";
|
import kodikImage from "assets/img/kodik.png";
|
||||||
import shikimoriImage from "assets/img/shikimori.png";
|
import shikimoriImage from "assets/img/shikimori.png";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectGroup,
|
SelectGroup,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectLabel,
|
SelectLabel,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue
|
SelectValue
|
||||||
} from "~/components/ui/select";
|
} from "~/components/ui/select";
|
||||||
import { toast } from "vue-sonner";
|
import { toast } from "vue-sonner";
|
||||||
import { Toaster } from "~/components/ui/sonner";
|
|
||||||
import { Navbar } from "~/components/ui/navbar";
|
|
||||||
import 'vue-sonner/style.css'
|
import 'vue-sonner/style.css'
|
||||||
import { Icon } from "#components";
|
import { Icon } from "#components";
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const searchProvider = ref('kodik')
|
const searchProvider = ref('kodik')
|
||||||
const displaySearchProvider = computed(() => {
|
const displaySearchProvider = computed(() => {
|
||||||
let label = '';
|
let label = '';
|
||||||
let image;
|
let image;
|
||||||
if (searchProvider.value === 'kodik') {
|
if (searchProvider.value === 'kodik') {
|
||||||
label = 'Kodik';
|
label = 'Kodik';
|
||||||
image = kodikImage;
|
image = kodikImage;
|
||||||
}
|
}
|
||||||
if (searchProvider.value === 'shikimori') {
|
if (searchProvider.value === 'shikimori') {
|
||||||
label = 'Shikimori';
|
label = 'Shikimori';
|
||||||
image = shikimoriImage;
|
image = shikimoriImage;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
label,
|
label,
|
||||||
image
|
image
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const search = defineModel<string>("")
|
const search = defineModel<string>("")
|
||||||
function querySearch() {
|
function querySearch() {
|
||||||
if (!search.value || search.value.trim() === "") {
|
if (!search.value || search.value.trim() === "") {
|
||||||
toast('Please enter a value');
|
toast('Please enter a value');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
router.push({ path: '/search', query: { title: search.value, provider: searchProvider.value } })
|
router.push({ path: '/search', query: { title: search.value, provider: searchProvider.value } })
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error('Navigation error:', err);
|
console.error('Navigation error:', err);
|
||||||
toast.error('Failed to navigate to search results');
|
toast.error('Failed to navigate to search results');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="bg-background flex-1 w-screen flex justify-center items-center gap-1">
|
||||||
<Toaster />
|
<form @submit.prevent="querySearch" class="flex items-center gap-2">
|
||||||
<Navbar />
|
<Select v-model="searchProvider">
|
||||||
|
<SelectTrigger>
|
||||||
<div class="bg-background h-lvh w-screen flex justify-center items-center gap-1">
|
<SelectValue placeholder="Select an provider">
|
||||||
<form @submit.prevent="querySearch" class="flex items-center gap-2">
|
<img :src="displaySearchProvider.image" alt="" class="w-5 h-5 rounded border border-gray-300">
|
||||||
<Select v-model="searchProvider">
|
<span>{{ displaySearchProvider.label }}</span>
|
||||||
<SelectTrigger>
|
</SelectValue>
|
||||||
<SelectValue placeholder="Select an provider">
|
</SelectTrigger>
|
||||||
<img :src="displaySearchProvider.image" alt="" class="w-5 h-5 rounded border border-gray-300">
|
<SelectContent>
|
||||||
<span>{{ displaySearchProvider.label }}</span>
|
<SelectGroup>
|
||||||
</SelectValue>
|
<SelectLabel>Providers</SelectLabel>
|
||||||
</SelectTrigger>
|
<SelectItem value="kodik">
|
||||||
<SelectContent>
|
<img :src="kodikImage" alt="" class="w-5 h-5 rounded border border-gray-300">
|
||||||
<SelectGroup>
|
<span>Kodik</span>
|
||||||
<SelectLabel>Providers</SelectLabel>
|
</SelectItem>
|
||||||
<SelectItem value="kodik">
|
<SelectItem value="shikimori">
|
||||||
<img :src="kodikImage" alt="" class="w-5 h-5 rounded border border-gray-300">
|
<img :src="shikimoriImage" alt="" class="w-5 h-5 rounded border border-gray-300">
|
||||||
<span>Kodik</span>
|
Shikimori
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="shikimori">
|
</SelectGroup>
|
||||||
<img :src="shikimoriImage" alt="" class="w-5 h-5 rounded border border-gray-300">
|
</SelectContent>
|
||||||
Shikimori
|
</Select>
|
||||||
</SelectItem>
|
<Input v-model="search" class="w-64" type="text" placeholder="Search anime..." />
|
||||||
</SelectGroup>
|
<Button type="submit">
|
||||||
</SelectContent>
|
<Icon name="heroicons:magnifying-glass-16-solid" class="w-6 h-6" />
|
||||||
</Select>
|
</Button>
|
||||||
<Input v-model="search" class="w-64" type="text" placeholder="Search anime..." />
|
</form>
|
||||||
<Button type="submit">
|
|
||||||
<Icon name="heroicons:magnifying-glass-16-solid" class="w-6 h-6" />
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
@ -9,50 +9,53 @@ const isLoading = ref(false)
|
|||||||
const error = ref<unknown>(null)
|
const error = ref<unknown>(null)
|
||||||
|
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
if (!searchQuery.value) return
|
if (!searchQuery.value) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
error.value = null
|
error.value = null
|
||||||
const response = await search({ title: searchQuery.value })
|
const response = await search({ title: searchQuery.value })
|
||||||
results.value = response.data.results || []
|
results.value = response.data.results || []
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = err
|
error.value = err
|
||||||
console.error('Search failed:', err)
|
console.error('Search failed:', err)
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="isLoading">
|
<div v-if="isLoading">
|
||||||
Loading results...
|
Loading results...
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="error">
|
<div v-if="error">
|
||||||
Error loading results: {{ error }}
|
Error loading results: {{ error }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="results.length > 0" class="grid grid-cols-[repeat(auto-fill,32rem)] justify-around gap-8 grid-flow-row">
|
<div v-if="results.length > 0"
|
||||||
<div v-for="item in results" :key="item.id" class="flex w-[32rem]">
|
class="grid grid-cols-[repeat(auto-fill,16rem)] justify-around gap-8 grid-flow-row">
|
||||||
<NuxtLink :to="`/anime/${item.id}`">
|
<div v-for="item in results" :key="item.id" class="flex w-[16rem]">
|
||||||
<img v-if="item.material_data?.anime_poster_url" :src="item.material_data.anime_poster_url" :alt="item.title"
|
<NuxtLink :to="`/anime/${item.id}`" class="w-full">
|
||||||
class="rounded-md">
|
<div v-if="item.material_data?.anime_poster_url" :style="{ 'background-image': 'url('+item.material_data.anime_poster_url+')' }"
|
||||||
<div>
|
:alt="item.title" class="flex items-end justify-end p-2 rounded-md bg-cover bg-center bg-no-repeat h-96">
|
||||||
<h3 className="text-2xl font-semibold tracking-tight">{{ item.title }}</h3>
|
<Button class="backdrop-blur-none bg-primary/80">2 episodes</Button>
|
||||||
<p v-if="item.material_data?.year">Year: {{ item.material_data.year }}</p>
|
</div>
|
||||||
<p v-if="item.material_data?.anime_kind">Type: {{ item.material_data.anime_kind }}</p>
|
<div>
|
||||||
<p v-if="item.material_data?.episodes_total">Episodes: {{ item.material_data.episodes_total }}
|
<h3 className="text-2xl font-semibold tracking-tight">{{ item.title }}</h3>
|
||||||
</p>
|
<p v-if="item.material_data?.year">Year: {{ item.material_data.year }}</p>
|
||||||
</div>
|
<p v-if="item.material_data?.anime_kind">Type: {{ item.material_data.anime_kind }}</p>
|
||||||
</NuxtLink>
|
<p v-if="item.material_data?.episodes_total">Episodes: {{ item.material_data.episodes_total }}
|
||||||
</div>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="!isLoading && !error && results.length === 0 && searchQuery">
|
<div v-if="!isLoading && !error && results.length === 0 && searchQuery">
|
||||||
No results found for "{{ searchQuery }}"
|
No results found for "{{ searchQuery }}"
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
Reference in New Issue
Block a user