106 lines
2.5 KiB
Vue
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>
|