This commit is contained in:
 Илья Шалонин 2025-10-29 13:43:43 +05:00
commit 8538ab969a
434 changed files with 44051 additions and 0 deletions

View file

@ -0,0 +1,148 @@
import slugify from "slugify";
export type ContentStructureItem = {
label: string;
path: string;
children: ContentStructureItem[];
isPage: boolean;
};
export function getContentStructure({
documents,
meta,
locale,
}: {
documents: {
path: string;
locale: string;
title: string;
}[];
meta: {
path: string;
locale: string;
data: Record<string, string | { title: string }>;
}[];
locale?: string;
}) {
const contentStructure: ContentStructureItem[] = [];
function addToContentItemArray(
contentItemsArray: ContentStructureItem[],
subPath: string,
item: (typeof documents)[number],
) {
const pathParts = item.path
.replace(new RegExp(`^${subPath}[/]*`), "")
.split("/");
const rootItemPath = subPath
? [subPath, pathParts[0]].join("/")
: pathParts[0];
let rootItem = contentItemsArray.find(
(contentItem) => contentItem.path === rootItemPath,
);
const isPage = pathParts.length === 1;
if (!rootItem) {
const path = isPage ? item.path : rootItemPath;
const metaData = meta
.filter((meta) => meta.path === subPath)
.sort((page) => (page.locale === locale ? -1 : 1))
.at(0)?.data[pathParts[0]];
const label = metaData
? typeof metaData === "string"
? metaData
: metaData.title
: (documents
.filter((page) => page.path === rootItemPath)
.sort((page) => (page.locale === locale ? -1 : 1))
.at(0)?.title ?? pathParts[0]);
rootItem = {
label,
path,
children: [],
isPage,
};
contentItemsArray.push(rootItem);
}
if (isPage && !rootItem.isPage) {
rootItem.isPage = true;
}
if (pathParts.length > 1) {
addToContentItemArray(rootItem.children, rootItemPath, item);
}
}
documents.forEach((page) => {
addToContentItemArray(contentStructure, "", page);
});
// recusrively sort items and their children
function sortContentItems(items: ContentStructureItem[], basePath = "") {
items.sort((a, b) => {
if (a.path === "") {
return -1;
}
if (b.path === "") {
return 1;
}
const aIndex = Object.entries(
meta
.filter((meta) => meta.path === basePath)
.sort((page) => (page.locale === locale ? -1 : 1))
.at(0)?.data ?? {},
).findIndex(([key]) => key === a.path.replace(`${basePath}/`, ""));
const bIndex = Object.entries(
meta
.filter((meta) => meta.path === basePath)
.sort((page) => (page.locale === locale ? -1 : 1))
.at(0)?.data ?? {},
).findIndex(([key]) => key === b.path.replace(`${basePath}/`, ""));
// use position index from meta file or put the item at the end of the list
return (
(aIndex > -1 ? aIndex : items.length) -
(bIndex > -1 ? bIndex : items.length)
);
});
items.forEach((item) => {
if (item.children.length) {
sortContentItems(item.children, item.path);
}
});
}
sortContentItems(contentStructure);
return contentStructure;
}
export function getActivePathFromUrlParam(path: string | string[]) {
return Array.isArray(path) ? path.join("/") : path || "";
}
export function getLocalizedDocumentWithFallback<
T extends { path: string; locale: string },
>(documents: T[], path: string, locale: string) {
return documents
.filter((doc) => doc.path === path)
.sort((doc) => (doc.locale === locale ? -1 : 1))[0];
}
export function slugifyHeadline(headline: string) {
return slugify(headline, {
lower: true,
replacement: "-",
trim: true,
strict: true,
remove: /[*+~.()'"!:@]/g,
});
}