Add no results components

This commit is contained in:
Eric Fennis
2023-05-11 00:04:27 +02:00
parent e1c22eeffb
commit dd7f594c53
3 changed files with 89 additions and 2 deletions
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ref, computed, defineAsyncComponent } from 'vue'
import type { IconEntity, Category } from '../../types'
import IconDetailOverlay from './IconDetailOverlay.vue'
import IconGrid from './IconGrid.vue'
@@ -51,6 +51,10 @@ const categories = computed(() => {
const activeIcon = computed(() =>
props.icons?.find((icon) => icon.name === activeIconName.value)
)
const NoResults = defineAsyncComponent(() =>
import('./NoResults.vue')
)
</script>
<template>
@@ -62,6 +66,11 @@ const activeIcon = computed(() =>
ref="searchInput"
/>
</StickyBar>
<NoResults
v-if="categories.length === 0"
:searchQuery="searchQuery"
@clear="searchQuery = ''"
/>
<section class="category" v-for="category in categories" :key="category.name">
<h2 class="title" :id="category.name">
<a class="header-anchor" :href="`#${category.name}`" :aria-label="`Permalink to &quot;${category.title}&quot;`">&ZeroWidthSpace;</a>
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { ref, computed, watch, defineAsyncComponent } from 'vue'
import type { IconEntity } from '../../types'
import { useMediaQuery, useOffsetPagination } from '@vueuse/core'
import IconDetailOverlay from './IconDetailOverlay.vue'
@@ -66,6 +66,10 @@ const activeIcon = computed(() =>
watch(searchQueryThrottled, (searchString) => {
currentPage.value = 1
})
const NoResults = defineAsyncComponent(() =>
import('./NoResults.vue')
)
</script>
<template>
@@ -77,6 +81,11 @@ watch(searchQueryThrottled, (searchString) => {
class="input-wrapper"
/>
</StickyBar>
<NoResults
v-if="paginatedIcons.length === 0"
:searchQuery="searchQuery"
@clear="searchQuery = ''"
/>
<IconGrid
overlayMode
:activeIcon="activeIconName"
@@ -0,0 +1,69 @@
<script setup lang="ts">
import { ref } from 'vue'
import { bird } from '../../../data/iconNodes'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { useEventListener } from '@vueuse/core'
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'
defineProps<{
searchQuery: string
}>()
defineEmits(['clear'])
const birdIcon = ref<HTMLElement>()
const Bird = createLucideIcon('bird', bird)
const flip = ref(false)
useEventListener(document, 'mousemove', (mouseEvent) => {
const { width, height, x, y } = birdIcon.value.getBoundingClientRect()
const centerX = (width / 2) + x
flip.value = mouseEvent.x < centerX
})
</script>
<template>
<div class="no-results">
<Bird class="bird-icon" ref="birdIcon" :class="{ flip }"/>
<h2 class="no-results-text">
No icons found for '{{ searchQuery }}'
</h2>
<VPButton text="Clear your search and try again" theme="alt" @click="$emit('clear')"/>
</div>
</template>
<style scoped>
.no-results {
display: flex;
flex-direction: column;
align-items: center;
}
.bird-icon {
width: 160px;
height: 160px;
color: var(--vp-c-neutral);
opacity: 0.8;
margin-top: 72px;
}
.bird-icon.flip {
transform: rotateY(180deg);
}
@media (min-width: 960px) {
.bird-icon {
width: 240px;
height: 240px;
}
}
.no-results-text {
line-height: 40px;
font-size: 24px;
margin-top: 24px;
margin-bottom: 32px;
text-align: center;
}
</style>