site-panzai/node_modules/@dxup/nuxt/dist/runtime/layouts.mjs
2026-11-13 13:57:04 +01:00

111 lines
3.3 KiB
JavaScript

import { injectionKey } from "./registry.mjs";
import { Fragment, computed, defineComponent, getCurrentInstance, h, inject, onActivated, onDeactivated, onScopeDispose, onUpdated, provide, shallowRef } from "vue";
import { NuxtLayout } from "#build/dxup/layouts.mjs";
// src/module/named-layout-slots/runtime/layouts.ts
var layouts_default = defineComponent((props, ctx) => {
const layers = [];
const slots = shallowRef(null);
let layerUid = 0;
let resolveReady;
function update() {
slots.value = layers.length > 1 ? mergeLayers([...layers]) : layers[0]?.slots ?? null;
}
provide(injectionKey, {
slots,
ready: new Promise((resolve) => {
resolveReady = resolve;
}),
use(value) {
const layer = {
slots: value,
uid: layerUid++
};
function add() {
if (layers.every((entry) => entry.slots !== value)) {
layers.push(layer);
update();
}
}
function remove() {
const index = layers.findIndex((entry) => entry.slots === value);
if (index !== -1) {
layers.splice(index, 1);
update();
}
}
add();
onScopeDispose(remove);
onDeactivated(remove);
onActivated(add);
resolveReady?.();
},
invalidate() {
slots.value = layers.length ? mergeLayers([...layers]) : null;
},
getOwner(name) {
for (let i = layers.length - 1; i >= 0; i--) if (layers[i].slots[name]) return layers[i].uid;
}
});
return () => h(NuxtLayout, props, ctx.slots);
});
/**
* Merges the slots of all mounted pages. The most recently mounted pages have
* a higher priority.
* @param layers
*/
function mergeLayers(layers) {
const merged = Object.create(null);
for (let i = layers.length - 1; i >= 0; i--) for (const key in layers[i].slots) if (!(key in merged)) Object.defineProperty(merged, key, {
enumerable: true,
configurable: true,
get: () => {
for (let j = layers.length - 1; j >= 0; j--) {
const slot = layers[j].slots[key];
if (slot) return slot;
}
}
});
return merged;
}
/**
* Returns the slots forwarded by the active page via named layout slots.
*/
function useLayoutSlots() {
const registry = inject(injectionKey, null);
return computed(() => registry?.slots.value ?? {});
}
const LayoutSlot = defineComponent({
props: { name: {
type: String,
required: true
} },
setup(props, ctx) {
const registry = inject(injectionKey, null);
const currentInstance = getCurrentInstance();
const render = () => {
const children = (registry?.slots.value?.[props.name] ?? currentInstance?.parent?.slots[props.name])?.(ctx.attrs) ?? ctx.slots.default?.() ?? [];
return h(Fragment, { key: registry?.getOwner(props.name) ?? -1 }, children);
};
if (import.meta.server && registry && !registry.slots.value?.[props.name]) return registry.ready.then(() => render);
return render;
}
});
const LayoutSlotsForward = defineComponent((props, ctx) => {
const registry = inject(injectionKey, null);
registry?.use(ctx.slots);
if (import.meta.client && registry) {
let keys = Object.keys(ctx.slots).join("\0");
onUpdated(() => {
const next = Object.keys(ctx.slots).join("\0");
if (next !== keys) {
keys = next;
registry.invalidate();
}
});
}
return () => {
const vnodes = ctx.slots.default?.();
return vnodes?.length === 1 ? vnodes[0] : vnodes;
};
});
export { LayoutSlot, LayoutSlotsForward, layouts_default as default, useLayoutSlots };