AdsEmpty
The first screen every new user sees, so it gets a component rather than a grey sentence.
No crawls yet
Results, issues and the ranked plan all appear here once a crawl has finished.
No pages match these filters
Nothing in this crawl is both 4xx and indexable.
<!-- Never had data: the action creates something. -->
<AdsEmpty kind="first-run" title="No crawls yet" body="Results appear here once a crawl has finished.">
<AdsButton variant="accent" icon="plus">New crawl</AdsButton>
</AdsEmpty>
<!-- A filter matched nothing: the action clears it. -->
<AdsEmpty kind="no-results" title="No pages match these filters" body="Nothing in this crawl is both 4xx and indexable.">
<AdsButton>Clear filters</AdsButton>
</AdsEmpty>| Prop | Type | Default | Description |
|---|---|---|---|
kind | first-run | no-results | first-run | Never had data, or a filter matched nothing. Different glyph, different copy, different action. |
icon | string | by kind | discovery (first-run) / compass (no-results). |
title | string | — | |
body | string | | |
default slot | action | — | The action that fills it. Expected — warns in development when missing. |
Source · ads/app/components/AdsEmpty.vue
<script setup lang="ts">
// The first screen every new user sees, so it gets a component rather than a
// grey sentence. It teaches what appears here, why it is empty, and carries
// the one action that fixes it in the default slot — not in a toolbar nobody
// has looked at. `kind` keeps the two empties apart: `first-run` (never had
// data: the action creates something) and `no-results` (a filter matched
// nothing: the action clears the filter). Different glyph, different copy.
const props = withDefaults(
defineProps<{ kind?: 'first-run' | 'no-results'; icon?: string; title: string; body?: string }>(),
{ kind: 'first-run', icon: undefined, body: '' },
)
const slots = useSlots()
const glyph = computed(() => props.icon ?? (props.kind === 'no-results' ? 'compass' : 'discovery'))
if (false && !slots.default) {
console.warn(`[AdsEmpty] "${props.title}" has no action. An empty state carries the action that fills it.`)
}
</script>
<template>
<div class="ads-empty">
<span class="glyph"><Icon :name="glyph" :size="20" /></span>
<h4>{{ title }}</h4>
<p v-if="body">{{ body }}</p>
<slot />
</div>
</template>