nuxt
Version:
291 lines (290 loc) • 7.21 kB
JavaScript
import { defineComponent, inject, onUnmounted, provide, reactive } from "vue";
import { useHead } from "#app/composables/head";
const HeadComponentCtxSymbol = Symbol("head-component");
const TagPositionProps = {
/**
* @deprecated Use tagPosition
*/
body: { type: Boolean, default: void 0 },
tagPosition: { type: String }
};
const normalizeProps = (_props) => {
const props = Object.fromEntries(
Object.entries(_props).filter(([_, value]) => value !== void 0)
);
if (typeof props.body !== "undefined") {
props.tagPosition = props.body ? "bodyClose" : "head";
}
if (typeof props.renderPriority !== "undefined") {
props.tagPriority = props.renderPriority;
}
return props;
};
function useHeadComponentCtx() {
return inject(HeadComponentCtxSymbol, createHeadComponentCtx, true);
}
function createHeadComponentCtx() {
const prev = inject(HeadComponentCtxSymbol, null);
if (prev) {
return prev;
}
const input = reactive({});
const entry = useHead(input);
const ctx = { input, entry };
provide(HeadComponentCtxSymbol, ctx);
return ctx;
}
const globalProps = {
accesskey: String,
autocapitalize: String,
autofocus: {
type: Boolean,
default: void 0
},
class: { type: [String, Object, Array], default: void 0 },
contenteditable: {
type: Boolean,
default: void 0
},
contextmenu: String,
dir: String,
draggable: {
type: Boolean,
default: void 0
},
enterkeyhint: String,
exportparts: String,
hidden: {
type: Boolean,
default: void 0
},
id: String,
inputmode: String,
is: String,
itemid: String,
itemprop: String,
itemref: String,
itemscope: String,
itemtype: String,
lang: String,
nonce: String,
part: String,
slot: String,
spellcheck: {
type: Boolean,
default: void 0
},
style: { type: [String, Object, Array], default: void 0 },
tabindex: String,
title: String,
translate: String,
/**
* @deprecated Use tagPriority
*/
renderPriority: [String, Number],
/**
* Unhead prop to modify the priority of the tag.
*/
tagPriority: { type: [String, Number] }
};
export const NoScript = defineComponent({
name: "NoScript",
inheritAttrs: false,
props: {
...globalProps,
...TagPositionProps,
title: String
},
setup(props, { slots }) {
const { input } = useHeadComponentCtx();
input.noscript ||= [];
const idx = input.noscript.push({}) - 1;
onUnmounted(() => input.noscript[idx] = null);
return () => {
const noscript = normalizeProps(props);
const slotVnodes = slots.default?.();
const textContent = slotVnodes ? slotVnodes.filter(({ children }) => children).map(({ children }) => children).join("") : "";
if (textContent) {
noscript.innerHTML = textContent;
}
input.noscript[idx] = noscript;
return null;
};
}
});
export const Link = defineComponent({
name: "Link",
inheritAttrs: false,
props: {
...globalProps,
...TagPositionProps,
as: String,
crossorigin: String,
disabled: Boolean,
fetchpriority: String,
href: String,
hreflang: String,
imagesizes: String,
imagesrcset: String,
integrity: String,
media: String,
prefetch: {
type: Boolean,
default: void 0
},
referrerpolicy: String,
rel: String,
sizes: String,
title: String,
type: String,
/** @deprecated **/
methods: String,
/** @deprecated **/
target: String
},
setup(props) {
const { input } = useHeadComponentCtx();
input.link ||= [];
const idx = input.link.push({}) - 1;
onUnmounted(() => input.link[idx] = null);
return () => {
input.link[idx] = normalizeProps(props);
return null;
};
}
});
export const Base = defineComponent({
name: "Base",
inheritAttrs: false,
props: {
...globalProps,
href: String,
target: String
},
setup(props) {
const { input } = useHeadComponentCtx();
onUnmounted(() => input.base = null);
return () => {
input.base = normalizeProps(props);
return null;
};
}
});
export const Title = defineComponent({
name: "Title",
inheritAttrs: false,
setup(_, { slots }) {
const { input } = useHeadComponentCtx();
onUnmounted(() => input.title = null);
return () => {
const defaultSlot = slots.default?.();
input.title = defaultSlot?.[0]?.children ? String(defaultSlot?.[0]?.children) : void 0;
if (import.meta.dev) {
if (defaultSlot && (defaultSlot.length > 1 || defaultSlot[0] && typeof defaultSlot[0].children !== "string")) {
console.error("<Title> can take only one string in its default slot.");
}
}
return null;
};
}
});
export const Meta = defineComponent({
name: "Meta",
inheritAttrs: false,
props: {
...globalProps,
charset: String,
content: String,
httpEquiv: String,
name: String,
property: String
},
setup(props) {
const { input } = useHeadComponentCtx();
input.meta ||= [];
const idx = input.meta.push({}) - 1;
onUnmounted(() => input.meta[idx] = null);
return () => {
const meta = { "http-equiv": props.httpEquiv, ...normalizeProps(props) };
if ("httpEquiv" in meta) {
delete meta.httpEquiv;
}
input.meta[idx] = meta;
return null;
};
}
});
export const Style = defineComponent({
name: "Style",
inheritAttrs: false,
props: {
...globalProps,
...TagPositionProps,
type: String,
media: String,
nonce: String,
title: String,
/** @deprecated **/
scoped: {
type: Boolean,
default: void 0
}
},
setup(props, { slots }) {
const { input } = useHeadComponentCtx();
input.style ||= [];
const idx = input.style.push({}) - 1;
onUnmounted(() => input.style[idx] = null);
return () => {
const style = normalizeProps(props);
const textContent = slots.default?.()?.[0]?.children;
if (textContent) {
if (import.meta.dev && typeof textContent !== "string") {
console.error("<Style> can only take a string in its default slot.");
}
input.style[idx] = style;
style.textContent = textContent;
}
return null;
};
}
});
export const Head = defineComponent({
name: "Head",
inheritAttrs: false,
setup: (_props, ctx) => {
createHeadComponentCtx();
return () => ctx.slots.default?.();
}
});
export const Html = defineComponent({
name: "Html",
inheritAttrs: false,
props: {
...globalProps,
manifest: String,
version: String,
xmlns: String
},
setup(_props, ctx) {
const { input } = useHeadComponentCtx();
onUnmounted(() => input.htmlAttrs = null);
return () => {
input.htmlAttrs = { ..._props, ...ctx.attrs };
return ctx.slots.default?.();
};
}
});
export const Body = defineComponent({
name: "Body",
inheritAttrs: false,
props: globalProps,
setup(_props, ctx) {
const { input } = useHeadComponentCtx();
onUnmounted(() => input.bodyAttrs = null);
return () => {
input.bodyAttrs = { ..._props, ...ctx.attrs };
return ctx.slots.default?.();
};
}
});