feat(ui): implement page header component

This commit is contained in:
upsiflu 2025-01-20 18:53:47 +02:00
parent 6daf2952ec
commit 541eca9c90
3 changed files with 115 additions and 0 deletions

View File

@ -0,0 +1,60 @@
<script setup lang="ts">
import type { RouterLinkProps } from 'vue-router'
import { useAttrs } from 'vue'
import Layout from '~/components/ui/Layout.vue'
import Spacer from '~/components/ui/Spacer.vue'
import Button from '~/components/ui/Button.vue'
import Link from '~/components/ui/Link.vue'
import Heading from '~/components/ui/Heading.vue'
const actionComponents =
{ Button, Link }
const props = defineProps<{
[M in 'no-items' | 'tiny-items' | 'small-items' | 'medium-items']?: true }
& { [H in 'h1' | 'h2' | 'h3']?: string }
& { action?: { text: string } & (RouterLinkProps | { onClick: (...args: any[]) => void | Promise<void> }) }>()
const heading =
props.h1 ? ({ h1: props.h1, pageHeading: true }) as const
: props.h2 ? ({ h2: props.h2 }) as const
: ({ h3: props.h3 }) as const
console.log("HEADING", heading)
const numberOfColumnsPerItem =
'noItems' in props && props.noItems ? 1 : 'tinyItems' in props && props.tinyItems ? 2 : 'smallItems' in props && props['smallItems'] ? 3 : 4
const { style, ...fallthroughProps } = useAttrs()
</script>
<template>
<Layout header flex>
<div v-if="$slots.image">
<slot name="image" />
</div>
<Layout stack no-gap style="flex-grow: 1;">
<Layout flex no-gap
style="align-self: baseline;"
>
<!-- Set distance between baseline and previous row -->
<Spacer v
:size="64"
style="align-self: baseline;"
/>
<Heading v-bind="heading" style="align-self: baseline; padding:0 0 24px 0; margin:0;"/>
<Spacer grow />
<!-- Action! You can either specify `to` or `onClick`. -->
<component v-if="action" :is="'onClick' in action ? actionComponents.Button : actionComponents.Link"
ghost force-underline thin-font min-content align-self="baseline"
:style="`margin-right: ${('primary' in props || 'secondary' in props || 'destructive' in props) ? '0px' : '-16px'}`"
v-bind="{...fallthroughProps, ...action}"
>
{{ action?.text }}
</component>
</Layout>
<slot />
</Layout>
</Layout>
</template>

View File

@ -52,6 +52,7 @@ export default defineConfig({
text: 'Layout', link: '/components/ui/layout/',
items: [
{ text: "Spacer", link: "/components/ui/layout/spacer" },
{ text: "Header", link: "/components/ui/layout/header" },
{ text: "Section", link: "/components/ui/layout/section" },
{ text: "Using `flex`", link: "/components/ui/layout/flex" },
{ text: "Using `stack`", link: "/components/ui/layout/stack" },

View File

@ -0,0 +1,54 @@
<script setup lang="ts">
import Header from '~/components/ui/Header.vue'
import Layout from '~/components/ui/Layout.vue'
import Spacer from '~/components/ui/Spacer.vue'
import Button from '~/components/ui/Button.vue'
</script>
```ts
import Header from "~/components/ui/Header.vue";
```
# Page header
Place the `Header` at the beginning of a page. Choose an appropriate heading level: `h1` or `h2` or `h3`. Choose `h1` unless the header is part of a page subsection or a modal.
```vue-html
<Header h1="My title" />
```
<Header h1="My title" />
## Add an image
Use the `<template #image>` slot to place a picture to the left of the header.
```vue-html
<Header h1="My title">
<template #image>
<img
src="https://images.unsplash.com/photo-1524650359799-842906ca1c06?ixlib=rb-1.2.1&dl=te-nguyen-Wt7XT1R6sjU-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb" />
</template>
</Header>
```
<Header h1="My title">
<template #image>
<img
style="width: 196px;"
src="https://images.unsplash.com/photo-1524650359799-842906ca1c06?ixlib=rb-1.2.1&dl=te-nguyen-Wt7XT1R6sjU-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb" />
</template>
<div style="height: 48px;">
My subtitle
</div>
<Layout flex gap-8>
<Button outline square>A</Button>
<Button outline square>B</Button>
<Spacer h grow />
<Button outline square>C</Button>
</Layout>
</Header>
## Add an action to the right of the heading
-> Use the `action` prop [which is the same as in the `Section` component](/components/ui/layout/section).