Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 1x 1x 1x 1x 1x 1x 1x 43x 1x 1x | <script setup lang="ts">
/**
* Unified page header for all top-level "main entity" views
* (Deployments, Apps, Kurse, Approvals, User-Settings, ...).
*
* Lifts the exact pattern from ``CoursesView.vue`` into a reusable
* component: a flex row with title + optional subtitle on the left
* and an actions slot on the right. ``mb-8`` separator below so the
* page body always sits the same distance from the title.
*
* Why a component and not a Tailwind preset: the actions slot is
* page-specific (create button, filter toggle, refresh, ...). A
* slot beats a prop here because each page wants different
* combinations of buttons, sometimes none.
*/
defineProps<{
title: string
/** Optional one-line subtitle in muted gray. Omitting it just
* removes the line — title sizing stays the same. */
subtitle?: string
}>()
</script>
<template>
<div class="flex items-center justify-between mb-8 gap-4 flex-wrap">
<div class="min-w-0">
<h1 class="text-3xl font-bold text-gray-900 mb-1">{{ title }}</h1>
<p v-if="subtitle" class="text-gray-500">{{ subtitle }}</p>
</div>
<!--
Actions go right of the title row. Multiple buttons stack
horizontally; the parent decides the order. When the slot is
empty Vue renders nothing here and the title row uses the full
width — no empty placeholder div.
-->
<div v-if="$slots.actions" class="flex items-center gap-2 shrink-0">
<slot name="actions" />
</div>
</div>
</template>
|