turned template into create cli

This commit is contained in:
2025-07-25 01:08:04 +02:00
parent 1b71b472a7
commit 028f287067
85 changed files with 3774 additions and 3292 deletions

View File

@@ -0,0 +1,243 @@
"use client";
/* eslint-disable @typescript-eslint/no-explicit-any */
import { PortableText, createDataAttribute, stegaClean } from "next-sanity";
import {
getFileAsset,
getImageAsset,
getImageDimensions,
SanityFileAsset,
} from "@sanity/asset-utils";
import type {
BlockContent,
Button as SanityButton,
SanityImageAsset,
} from "@/sanity/sanity.types";
import { client } from "@/sanity/client";
import LinkButton from "./link-button";
import Image from "next/image";
import Link from "next/link";
import { DownloadIcon } from "lucide-react";
import { useDeconstructLink } from "@/lib/link-client";
import { dynamicHeight, generateImageUrl } from "@/lib/image-url";
import { Button } from "./ui/button";
import { sanityConnection } from "@repo/sanity-connection";
const { projectId, dataset } = client.config();
export function Callout(props: any) {
if (!props) {
return null;
}
return (
<div className="bg-secondary-500 text-secondary-50 shadow-lg rounded-md p-6 border my-6 border-gray-200">
<PortableText
value={props.value.content}
components={{
block: {
h1: ({ children }: any) => <h1>{children}</h1>,
h2: ({ children }: any) => <h2>{children}</h2>,
h3: ({ children }: any) => <h3>{children}</h3>,
h4: ({ children }: any) => <h4>{children}</h4>,
h5: ({ children }: any) => <h5>{children}</h5>,
h6: ({ children }: any) => <h6>{children}</h6>,
},
}}
/>
</div>
);
}
export function PortableButton(value: { value: SanityButton }) {
const linkData = useDeconstructLink(value.value.link);
return (
<div className="flex justify-left">
<LinkButton
className="px-5"
text={value.value.text ?? ""}
color={"default"}
linkData={linkData}
/>
</div>
);
}
export function PortableImage({
value,
isInline,
}: {
value: SanityImageAsset;
isInline: boolean;
}) {
const { width, height } = getImageDimensions(value);
const imageAsset = getImageAsset(value, {
projectId: sanityConnection.projectId ?? "",
dataset: sanityConnection.dataset ?? "",
});
const attr = createDataAttribute({
id: imageAsset._id,
type: imageAsset._type,
workspace: "production",
});
return (
<Image
data-sanity={attr("body")}
src={generateImageUrl(value)}
width={
isInline ? (width >= 100 ? 100 : width) : width >= 1200 ? 1200 : width
}
height={dynamicHeight(height, width, isInline)}
alt={value.altText || " "}
loading="lazy"
className="border rounded-md shadow-md"
style={{
display: isInline ? "inline-block" : "block",
aspectRatio: width / height,
maxHeight: "400px",
maxWidth: "100%",
width: "auto",
}}
/>
);
}
export function PortableFile({ value }: { value: SanityFileAsset }) {
const fileAsset = getFileAsset(value, {
projectId: projectId ?? "",
dataset: dataset ?? "",
});
const attr = createDataAttribute({
id: fileAsset._id,
type: fileAsset._type,
workspace: "production",
});
const filename: string =
fileAsset?.originalFilename ||
`${fileAsset.extension.charAt(0).toUpperCase() + fileAsset.extension?.slice(1)} herunterladen`;
return (
<div className="flex justify-left">
<LinkButton
linkData={{ href: fileAsset.url, target: "" }}
data-sanity={attr("body")}
target="_blank"
rel="noopener noreferrer"
download
showIcon={false}
extraIcon={DownloadIcon}
text={
filename.length > 20
? `${filename.slice(0, 10)}...${filename.slice(-10)}`
: filename
}
/>
</div>
);
}
function CustomLink(props: { children: React.ReactNode; value?: any }) {
const { value, children } = props;
let linkUrl = null;
let target = "_self";
if (value?.href) {
const href = value.href;
if (href.type === "external" && href.url) {
linkUrl = href.url;
target = href.blank ? "_blank" : "_self";
} else if (href.type === "internal" || href._type === "reference") {
const resolved = useDeconstructLink(href);
if (resolved) {
const ButtonComponent = Button as any;
return (
<ButtonComponent variant="link" className="p-0 h-auto font-normal" asChild>
<Link href={resolved}>
{children}
</Link>
</ButtonComponent>
);
}
} else if (typeof href === "string") {
linkUrl = href;
}
}
if (linkUrl) {
const ButtonComponent = Button as any;
return (
<ButtonComponent variant="link" className="p-0 h-auto font-normal" asChild>
<Link
href={linkUrl}
target={target}
rel={target === "_blank" ? "noopener noreferrer" : undefined}
>
{children}
</Link>
</ButtonComponent>
);
}
return <>{children}</>;
}
const components = {
types: {
image: PortableImage,
button: PortableButton,
callout: Callout,
file: PortableFile,
},
block: {
h1: ({ children }: any) => <h1>{children}</h1>,
h2: ({ children }: any) => <h2>{children}</h2>,
h3: ({ children }: any) => <h3>{children}</h3>,
h4: ({ children }: any) => <h4>{children}</h4>,
h5: ({ children }: any) => <h5>{children}</h5>,
h6: ({ children }: any) => <h6>{children}</h6>,
},
marks: {
left: ({ children }: { children: React.ReactNode }) => (
<span style={{ textAlign: "left", width: "100%", display: "block" }}>
{children}
</span>
),
center: ({ children }: { children: React.ReactNode }) => (
<span style={{ textAlign: "center", width: "100%", display: "block" }}>
{children}
</span>
),
right: ({ children }: { children: React.ReactNode }) => (
<span style={{ textAlign: "right", width: "100%", display: "block" }}>
{children}
</span>
),
textColor: ({
children,
value = { value: "" },
}: {
children: React.ReactNode;
value?: { value: string };
}) => <span style={{ color: stegaClean(value.value) }}>{children}</span>,
highlightColor: ({
children,
value = { value: "" },
}: {
children: React.ReactNode;
value?: { value: string };
}) => (
<span style={{ background: stegaClean(value.value) }}>{children}</span>
),
link: CustomLink,
},
};
export default function SanityBlock({ body }: { body: BlockContent }) {
if (!body) {
return null;
}
return <PortableText value={body} components={components} />;
}