Files
RenewCMS/web/src/components/AuthShell.vue
T

106 lines
2.5 KiB
Vue

<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, useSlots, watch } from 'vue'
import AppLogo from '@/components/AppLogo.vue'
import { isInAppNavigation } from '@/composables/useAuthShellNavigation'
const props = defineProps<{
title: string
description?: string
}>()
const slots = useSlots()
const titleEl = ref<HTMLHeadingElement | null>(null)
const belowSm = ref(false)
let mediaQuery: MediaQueryList | null = null
function updateBelowSm() {
belowSm.value = mediaQuery?.matches ?? false
}
onMounted(() => {
if (typeof window.matchMedia === 'function') {
mediaQuery = window.matchMedia('(max-width: 39.9975rem)')
updateBelowSm()
mediaQuery.addEventListener('change', updateBelowSm)
}
if (isInAppNavigation()) {
titleEl.value?.focus()
}
})
onUnmounted(() => {
mediaQuery?.removeEventListener('change', updateBelowSm)
})
const controlSize = computed(() => (belowSm.value ? 'xl' : undefined))
const controlClass = computed(() => (belowSm.value ? '!h-11 !text-base' : '!h-10 !text-sm'))
const mainClass = computed(() => [
'flex min-h-screen flex-col items-center',
belowSm.value ? 'bg-default' : 'bg-muted pt-[12vh]',
])
const cardClass = computed(() => (belowSm.value ? 'w-full' : 'w-full max-w-[400px]'))
const cardUi = computed(() => ({
root: belowSm.value ? 'ring-0' : '',
container: belowSm.value ? 'pt-2 px-5 pb-0' : 'p-10',
header: 'mb-6',
title: 'text-xl font-semibold leading-7',
}))
const footerClass = computed(() => [
'mt-4 flex w-full items-center justify-between',
belowSm.value ? 'pt-0 px-5 pb-6' : 'p-0',
cardClass.value,
])
const colorModeSelectUi = { trailing: 'hidden' }
watch(
() => props.title,
(title) => {
document.title = `${title} · RenewCMS`
},
{ immediate: true },
)
watch(
() => props.title,
() => {
titleEl.value?.focus()
},
)
</script>
<template>
<main :class="mainClass">
<UPageCard
variant="outline"
:description="description"
:ui="cardUi"
data-testid="auth-card"
:class="cardClass"
>
<template #header>
<AppLogo :size="32" />
</template>
<template #title>
<h1 ref="titleEl" tabindex="-1" class="outline-none">{{ title }}</h1>
</template>
<div v-if="slots.notice" class="mb-4">
<slot name="notice" />
</div>
<slot :control-size="controlSize" :control-class="controlClass" />
</UPageCard>
<footer :class="footerClass">
<slot name="footer" />
<UColorModeSelect size="sm" class="ms-auto" :ui="colorModeSelectUi" />
</footer>
</main>
</template>