UNPKG

nuxt

Version:

Nuxt is a free and open-source framework with an intuitive and extendable way to create type-safe, performant and production-grade full-stack web applications and websites with Vue.js.

145 lines (144 loc) 4.57 kB
import { Suspense, Transition, computed, defineComponent, h, inject, mergeProps, nextTick, onMounted, provide, ref, unref } from "vue"; import { useRoute, useRouter } from "../composables/router.js"; import { useNuxtApp } from "../nuxt.js"; import { _wrapIf } from "./utils.js"; import { LayoutMetaSymbol, PageRouteSymbol } from "./injections.js"; import { useRoute as useVueRouterRoute } from "#build/pages"; import layouts from "#build/layouts"; import { appLayoutTransition as defaultLayoutTransition } from "#build/nuxt.config.mjs"; const LayoutLoader = defineComponent({ name: "LayoutLoader", inheritAttrs: false, props: { name: String, layoutProps: Object }, async setup(props, context) { const LayoutComponent = await layouts[props.name]().then((r) => r.default || r); return () => h(LayoutComponent, props.layoutProps, context.slots); } }); export default defineComponent({ name: "NuxtLayout", inheritAttrs: false, props: { name: { type: [String, Boolean, Object], default: null }, fallback: { type: [String, Object], default: null } }, setup(props, context) { const nuxtApp = useNuxtApp(); const injectedRoute = inject(PageRouteSymbol); const route = injectedRoute === useRoute() ? useVueRouterRoute() : injectedRoute; const layout = computed(() => { let layout2 = unref(props.name) ?? route.meta.layout ?? "default"; if (layout2 && !(layout2 in layouts)) { if (import.meta.dev && layout2 !== "default") { console.warn(`Invalid layout \`${layout2}\` selected.`); } if (props.fallback) { layout2 = unref(props.fallback); } } return layout2; }); const layoutRef = ref(); context.expose({ layoutRef }); const done = nuxtApp.deferHydration(); if (import.meta.client && nuxtApp.isHydrating) { const removeErrorHook = nuxtApp.hooks.hookOnce("app:error", done); useRouter().beforeEach(removeErrorHook); } if (import.meta.dev) { nuxtApp._isNuxtLayoutUsed = true; } return () => { const hasLayout = layout.value && layout.value in layouts; const transitionProps = route.meta.layoutTransition ?? defaultLayoutTransition; return _wrapIf(Transition, hasLayout && transitionProps, { default: () => h(Suspense, { suspensible: true, onResolve: () => { nextTick(done); } }, { default: () => h( LayoutProvider, { layoutProps: mergeProps(context.attrs, { ref: layoutRef }), key: layout.value || void 0, name: layout.value, shouldProvide: !props.name, hasTransition: !!transitionProps }, context.slots ) }) }).default(); }; } }); const LayoutProvider = defineComponent({ name: "NuxtLayoutProvider", inheritAttrs: false, props: { name: { type: [String, Boolean] }, layoutProps: { type: Object }, hasTransition: { type: Boolean }, shouldProvide: { type: Boolean } }, setup(props, context) { const name = props.name; if (props.shouldProvide) { provide(LayoutMetaSymbol, { isCurrent: (route) => name === (route.meta.layout ?? "default") }); } let vnode; if (import.meta.dev && import.meta.client) { onMounted(() => { nextTick(() => { if (["#comment", "#text"].includes(vnode?.el?.nodeName)) { if (name) { console.warn(`[nuxt] \`${name}\` layout does not have a single root node and will cause errors when navigating between routes.`); } else { console.warn("[nuxt] `<NuxtLayout>` needs to be passed a single root node in its default slot."); } } }); }); } return () => { if (!name || typeof name === "string" && !(name in layouts)) { if (import.meta.dev && import.meta.client && props.hasTransition) { vnode = context.slots.default?.(); return vnode; } return context.slots.default?.(); } if (import.meta.dev && import.meta.client && props.hasTransition) { vnode = h( LayoutLoader, { key: name, layoutProps: props.layoutProps, name }, context.slots ); return vnode; } return h( LayoutLoader, { key: name, layoutProps: props.layoutProps, name }, context.slots ); }; } });