@asteroidcms/core-utils 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +168 -1
- package/dist/client.cjs +505 -0
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +309 -1
- package/dist/client.d.ts +309 -1
- package/dist/client.js +500 -3
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +379 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +186 -1
- package/dist/index.d.ts +186 -1
- package/dist/index.js +368 -1
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +211 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +114 -0
- package/dist/next.d.ts +114 -0
- package/dist/next.js +201 -0
- package/dist/next.js.map +1 -0
- package/dist/server.cjs +699 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +342 -0
- package/dist/server.d.ts +342 -0
- package/dist/server.js +669 -0
- package/dist/server.js.map +1 -0
- package/package.json +23 -2
package/dist/client.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { createContext, memo, useMemo, useRef, useState, useLayoutEffect, Fragment, createElement, useContext } from 'react';
|
|
2
|
+
import { createContext, memo, useMemo, useRef, useState, useLayoutEffect, Fragment, createElement, useContext, useEffect } from 'react';
|
|
3
3
|
import { ApolloProvider, useQuery, useMutation } from '@apollo/client/react';
|
|
4
4
|
import { gql, HttpLink, ApolloClient, InMemoryCache, ApolloLink, CombinedGraphQLErrors, CombinedProtocolErrors } from '@apollo/client';
|
|
5
5
|
import { SetContextLink } from '@apollo/client/link/context';
|
|
6
6
|
import { ErrorLink } from '@apollo/client/link/error';
|
|
7
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
7
|
+
import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
8
8
|
import { createPortal } from 'react-dom';
|
|
9
9
|
import hljs from 'highlight.js/lib/common';
|
|
10
|
+
import { useRouter, usePathname, useSearchParams } from 'next/navigation';
|
|
10
11
|
|
|
11
12
|
var AsteroidCMSContext = createContext(null);
|
|
12
13
|
function useAsteroidCMSConfig() {
|
|
@@ -1728,7 +1729,503 @@ function extractHeadingsFromElement(root, options = {}) {
|
|
|
1728
1729
|
});
|
|
1729
1730
|
return out;
|
|
1730
1731
|
}
|
|
1732
|
+
function setMeta(attr, key, content) {
|
|
1733
|
+
let element = document.head.querySelector(
|
|
1734
|
+
`meta[${attr}="${key}"]`
|
|
1735
|
+
);
|
|
1736
|
+
if (!element) {
|
|
1737
|
+
if (!content) return;
|
|
1738
|
+
element = document.createElement("meta");
|
|
1739
|
+
element.setAttribute(attr, key);
|
|
1740
|
+
document.head.appendChild(element);
|
|
1741
|
+
} else if (!content) {
|
|
1742
|
+
element.remove();
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
element.setAttribute("content", content);
|
|
1746
|
+
}
|
|
1747
|
+
function setCanonical(url) {
|
|
1748
|
+
let canonical = document.head.querySelector(
|
|
1749
|
+
'link[rel="canonical"]'
|
|
1750
|
+
);
|
|
1751
|
+
if (!canonical) {
|
|
1752
|
+
canonical = document.createElement("link");
|
|
1753
|
+
canonical.rel = "canonical";
|
|
1754
|
+
document.head.appendChild(canonical);
|
|
1755
|
+
}
|
|
1756
|
+
canonical.href = url;
|
|
1757
|
+
}
|
|
1758
|
+
function JsonLd({ data }) {
|
|
1759
|
+
if (!data) return null;
|
|
1760
|
+
const html = JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
1761
|
+
return /* @__PURE__ */ jsx(
|
|
1762
|
+
"script",
|
|
1763
|
+
{
|
|
1764
|
+
type: "application/ld+json",
|
|
1765
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
1766
|
+
}
|
|
1767
|
+
);
|
|
1768
|
+
}
|
|
1769
|
+
function Seo({
|
|
1770
|
+
title,
|
|
1771
|
+
description,
|
|
1772
|
+
url,
|
|
1773
|
+
siteName,
|
|
1774
|
+
keywords,
|
|
1775
|
+
twitter,
|
|
1776
|
+
image,
|
|
1777
|
+
noindex
|
|
1778
|
+
}) {
|
|
1779
|
+
useEffect(() => {
|
|
1780
|
+
document.title = title;
|
|
1781
|
+
setMeta("name", "description", description);
|
|
1782
|
+
setCanonical(url);
|
|
1783
|
+
setMeta("property", "og:title", title);
|
|
1784
|
+
setMeta("property", "og:description", description);
|
|
1785
|
+
setMeta("property", "og:url", url);
|
|
1786
|
+
setMeta("property", "og:site_name", siteName || "");
|
|
1787
|
+
setMeta("name", "keywords", keywords || "");
|
|
1788
|
+
setMeta("name", "robots", noindex ? "noindex" : "index");
|
|
1789
|
+
setMeta("name", "twitter:card", image ? "summary_large_image" : "summary");
|
|
1790
|
+
setMeta("name", "twitter:title", title);
|
|
1791
|
+
setMeta("name", "twitter:description", description);
|
|
1792
|
+
setMeta("name", "twitter:site", twitter || "");
|
|
1793
|
+
setMeta("property", "og:image", image || "");
|
|
1794
|
+
setMeta("name", "twitter:image", image || "");
|
|
1795
|
+
}, [title, description, url, siteName, keywords, twitter, image, noindex]);
|
|
1796
|
+
return null;
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
// src/seo/seo.builders.ts
|
|
1800
|
+
function applyTitleTemplate(config, title) {
|
|
1801
|
+
return config.titleTemplate ? config.titleTemplate(title) : `${title} | ${config.siteName}`;
|
|
1802
|
+
}
|
|
1803
|
+
function buildOgImageUrl(config, params) {
|
|
1804
|
+
if (config.getOgImageUrl) {
|
|
1805
|
+
return config.getOgImageUrl(params);
|
|
1806
|
+
}
|
|
1807
|
+
const palette = config.ogImage?.palette;
|
|
1808
|
+
if (!palette) return void 0;
|
|
1809
|
+
const apiPath = config.ogImage?.apiPath ?? "/api/og";
|
|
1810
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
1811
|
+
const searchParams = new URLSearchParams({
|
|
1812
|
+
title: params.title,
|
|
1813
|
+
type: params.type ?? "article",
|
|
1814
|
+
siteName: config.siteName,
|
|
1815
|
+
bg: palette.background,
|
|
1816
|
+
fg: palette.foreground,
|
|
1817
|
+
accent: palette.accent
|
|
1818
|
+
});
|
|
1819
|
+
if (params.subtitle?.trim()) searchParams.set("subtitle", params.subtitle.trim());
|
|
1820
|
+
if (params.eyebrow?.trim()) searchParams.set("eyebrow", params.eyebrow.trim());
|
|
1821
|
+
if (palette.accentMuted) searchParams.set("accentMuted", palette.accentMuted);
|
|
1822
|
+
if (palette.mutedText) searchParams.set("muted", palette.mutedText);
|
|
1823
|
+
return `${base}${apiPath}?${searchParams.toString()}`;
|
|
1824
|
+
}
|
|
1825
|
+
function resolveArticleImage(post, config) {
|
|
1826
|
+
const featuredImage = config.cmsUrl ? cmsImage(post.featured_image, { cmsUrl: config.cmsUrl }) : "";
|
|
1827
|
+
if (featuredImage) return featuredImage;
|
|
1828
|
+
const description = post.meta_description?.trim() || post.description?.trim() || config.defaultDescription;
|
|
1829
|
+
return buildOgImageUrl(config, {
|
|
1830
|
+
title: post.title,
|
|
1831
|
+
subtitle: description,
|
|
1832
|
+
eyebrow: config.contentLabel ?? "Article",
|
|
1833
|
+
type: "article"
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
function buildArticleSeoValues(post, config, slug, options) {
|
|
1837
|
+
const articlePath = config.articlePath ?? "/blog";
|
|
1838
|
+
const url = `${config.baseUrl.replace(/\/$/, "")}${articlePath}/${slug}`;
|
|
1839
|
+
const description = post.meta_description?.trim() || post.description?.trim() || config.defaultDescription || `Read the latest from ${config.siteName}.`;
|
|
1840
|
+
return {
|
|
1841
|
+
title: applyTitleTemplate(config, post.title),
|
|
1842
|
+
siteName: config.siteName,
|
|
1843
|
+
twitter: config.twitter ?? "",
|
|
1844
|
+
description,
|
|
1845
|
+
url,
|
|
1846
|
+
keywords: config.defaultKeywords ?? post.title,
|
|
1847
|
+
image: resolveArticleImage(post, config),
|
|
1848
|
+
noindex: options?.noindex ?? config.noindex,
|
|
1849
|
+
manifestUrl: config.manifestUrl
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1852
|
+
function buildArticleListingSeoValues(config, options) {
|
|
1853
|
+
const articlePath = config.articlePath ?? "/blog";
|
|
1854
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
1855
|
+
const label = config.contentLabel ?? "Articles";
|
|
1856
|
+
const categoryName = options?.categoryName?.trim();
|
|
1857
|
+
const categorySlug = options?.categorySlug?.trim();
|
|
1858
|
+
const titleText = categoryName ? `${categoryName} ${label}` : label;
|
|
1859
|
+
const description = categoryName ? `Explore ${categoryName} ${label.toLowerCase()}, guides, and the latest updates from ${config.siteName}.` : config.defaultDescription || `Browse ${label.toLowerCase()}, insights, and the latest updates from ${config.siteName}.`;
|
|
1860
|
+
const url = categorySlug ? `${base}${articlePath}/category/${categorySlug}` : `${base}${articlePath}`;
|
|
1861
|
+
return {
|
|
1862
|
+
title: applyTitleTemplate(config, titleText),
|
|
1863
|
+
siteName: config.siteName,
|
|
1864
|
+
twitter: config.twitter ?? "",
|
|
1865
|
+
description,
|
|
1866
|
+
url,
|
|
1867
|
+
keywords: config.defaultKeywords ?? (categoryName ? `${categoryName}, ${config.siteName}` : `${config.siteName} ${label.toLowerCase()}`),
|
|
1868
|
+
image: buildOgImageUrl(config, {
|
|
1869
|
+
title: titleText,
|
|
1870
|
+
subtitle: description,
|
|
1871
|
+
eyebrow: categoryName ? "Category" : label,
|
|
1872
|
+
type: "listing"
|
|
1873
|
+
}),
|
|
1874
|
+
noindex: options?.noindex ?? config.noindex,
|
|
1875
|
+
manifestUrl: config.manifestUrl
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
function seoValuesToClientProps(values) {
|
|
1879
|
+
return {
|
|
1880
|
+
title: values.title,
|
|
1881
|
+
description: values.description,
|
|
1882
|
+
url: values.url,
|
|
1883
|
+
siteName: values.siteName,
|
|
1884
|
+
keywords: values.keywords,
|
|
1885
|
+
twitter: values.twitter,
|
|
1886
|
+
image: values.image,
|
|
1887
|
+
noindex: values.noindex
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
// src/seo/jsonld.ts
|
|
1892
|
+
function buildArticleJsonLd(props) {
|
|
1893
|
+
return {
|
|
1894
|
+
"@context": "https://schema.org",
|
|
1895
|
+
"@type": props.articleType ?? "Article",
|
|
1896
|
+
headline: props.title,
|
|
1897
|
+
description: props.description,
|
|
1898
|
+
url: props.url,
|
|
1899
|
+
...props.image ? { image: props.image } : {},
|
|
1900
|
+
...props.publishedTime ? { datePublished: props.publishedTime } : {},
|
|
1901
|
+
...props.category ? { articleSection: props.category } : {},
|
|
1902
|
+
...props.tags && props.tags.length > 0 ? { keywords: props.tags.join(", ") } : {},
|
|
1903
|
+
author: { "@type": "Person", name: props.authorName || props.siteName },
|
|
1904
|
+
publisher: { "@id": `${props.siteUrl}/#organization` },
|
|
1905
|
+
isPartOf: { "@id": `${props.siteUrl}/#website` },
|
|
1906
|
+
inLanguage: "en-US"
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
function buildCollectionJsonLd(props) {
|
|
1910
|
+
return {
|
|
1911
|
+
"@context": "https://schema.org",
|
|
1912
|
+
"@type": "CollectionPage",
|
|
1913
|
+
name: props.name,
|
|
1914
|
+
description: props.description,
|
|
1915
|
+
url: props.url,
|
|
1916
|
+
isPartOf: { "@id": `${props.siteUrl}/#website` },
|
|
1917
|
+
publisher: { "@id": `${props.siteUrl}/#organization` },
|
|
1918
|
+
inLanguage: "en-US"
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
function renderArticleBody(args) {
|
|
1922
|
+
const { post, cmsImage: cmsImage2, relatedPosts, seoNode, jsonLdNode, renderProps: r } = args;
|
|
1923
|
+
const slot = { post, cmsImage: cmsImage2 };
|
|
1924
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1925
|
+
seoNode,
|
|
1926
|
+
jsonLdNode,
|
|
1927
|
+
r.backLink,
|
|
1928
|
+
r.renderPreArticle?.(slot),
|
|
1929
|
+
r.renderHeader?.(slot),
|
|
1930
|
+
r.renderMeta?.(slot),
|
|
1931
|
+
r.renderDescription?.(slot),
|
|
1932
|
+
r.renderFeaturedImage?.(slot),
|
|
1933
|
+
r.renderToc?.(slot),
|
|
1934
|
+
r.renderContent?.(slot),
|
|
1935
|
+
r.renderMidArticle?.(slot),
|
|
1936
|
+
r.renderTags?.(slot),
|
|
1937
|
+
r.renderAuthorDetails?.(slot),
|
|
1938
|
+
r.renderRelatedPosts?.({ post, relatedPosts, cmsImage: cmsImage2 }),
|
|
1939
|
+
r.renderCTA?.(slot),
|
|
1940
|
+
r.renderPostArticle?.(slot)
|
|
1941
|
+
] });
|
|
1942
|
+
}
|
|
1943
|
+
function AsteroidArticlePage(props) {
|
|
1944
|
+
const {
|
|
1945
|
+
slug,
|
|
1946
|
+
useArticle,
|
|
1947
|
+
seo,
|
|
1948
|
+
articleType,
|
|
1949
|
+
noindex,
|
|
1950
|
+
relatedPosts = [],
|
|
1951
|
+
renderRoot,
|
|
1952
|
+
renderSkeleton,
|
|
1953
|
+
renderError,
|
|
1954
|
+
renderJsonLd,
|
|
1955
|
+
children,
|
|
1956
|
+
...bodyRenderProps
|
|
1957
|
+
} = props;
|
|
1958
|
+
const { data: article, loading, error } = useArticle(slug);
|
|
1959
|
+
const cmsConfig = useContext(AsteroidCMSContext);
|
|
1960
|
+
const cmsImage2 = useCmsImage();
|
|
1961
|
+
const seoConfig = seo && !seo.cmsUrl && cmsConfig?.cmsUrl ? { ...seo, cmsUrl: cmsConfig.cmsUrl } : seo;
|
|
1962
|
+
if (children) return /* @__PURE__ */ jsx(Fragment$1, { children: children({ data: article, loading, error }) });
|
|
1963
|
+
if (loading) {
|
|
1964
|
+
const body2 = renderSkeleton?.() ?? null;
|
|
1965
|
+
return renderRoot ? /* @__PURE__ */ jsx(Fragment$1, { children: renderRoot({ children: body2 }) }) : /* @__PURE__ */ jsx(Fragment$1, { children: body2 });
|
|
1966
|
+
}
|
|
1967
|
+
if (!article || error) {
|
|
1968
|
+
const body2 = renderError?.({ error, reason: error ? "error" : "not-found" }) ?? null;
|
|
1969
|
+
return renderRoot ? /* @__PURE__ */ jsx(Fragment$1, { children: renderRoot({ children: body2 }) }) : /* @__PURE__ */ jsx(Fragment$1, { children: body2 });
|
|
1970
|
+
}
|
|
1971
|
+
const seoValues = seoConfig ? buildArticleSeoValues(article, seoConfig, slug, { noindex }) : null;
|
|
1972
|
+
const seoNode = seoValues ? /* @__PURE__ */ jsx(Seo, { ...seoValuesToClientProps(seoValues) }) : null;
|
|
1973
|
+
const jsonLdNode = seoConfig ? renderJsonLd?.({ post: article }) ?? /* @__PURE__ */ jsx(
|
|
1974
|
+
JsonLd,
|
|
1975
|
+
{
|
|
1976
|
+
data: buildArticleJsonLd({
|
|
1977
|
+
title: article.title,
|
|
1978
|
+
description: article.description || seoConfig.defaultDescription || "",
|
|
1979
|
+
url: `${(seoConfig.baseUrl || "").replace(/\/$/, "")}${seoConfig.articlePath ?? "/blog"}/${slug}`,
|
|
1980
|
+
siteName: seoConfig.siteName,
|
|
1981
|
+
siteUrl: (seoConfig.baseUrl || "").replace(/\/$/, ""),
|
|
1982
|
+
articleType,
|
|
1983
|
+
image: seoValues?.image,
|
|
1984
|
+
authorName: article.author?.name,
|
|
1985
|
+
publishedTime: article.published_date || void 0,
|
|
1986
|
+
tags: article.tags?.split(",").map((t) => t.trim()).filter(Boolean),
|
|
1987
|
+
category: article.category?.name
|
|
1988
|
+
})
|
|
1989
|
+
}
|
|
1990
|
+
) : null;
|
|
1991
|
+
const body = renderArticleBody({
|
|
1992
|
+
post: article,
|
|
1993
|
+
cmsImage: cmsImage2,
|
|
1994
|
+
relatedPosts,
|
|
1995
|
+
seoNode,
|
|
1996
|
+
jsonLdNode,
|
|
1997
|
+
renderProps: bodyRenderProps
|
|
1998
|
+
});
|
|
1999
|
+
return renderRoot ? /* @__PURE__ */ jsx(Fragment$1, { children: renderRoot({ children: body }) }) : /* @__PURE__ */ jsx(Fragment$1, { children: body });
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
// src/components/articles/articles.state.ts
|
|
2003
|
+
function defaultGetCategoryName(post) {
|
|
2004
|
+
return post.category?.name?.trim() || void 0;
|
|
2005
|
+
}
|
|
2006
|
+
function defaultGroupPostsByCategory(posts) {
|
|
2007
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2008
|
+
for (const post of posts) {
|
|
2009
|
+
const categoryName = defaultGetCategoryName(post) || "Other";
|
|
2010
|
+
const categorySlug = post.category?.slug || "other";
|
|
2011
|
+
const existing = groups.get(categorySlug);
|
|
2012
|
+
if (existing) {
|
|
2013
|
+
existing.posts.push(post);
|
|
2014
|
+
} else {
|
|
2015
|
+
groups.set(categorySlug, { categoryName, categorySlug, posts: [post] });
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
return Array.from(groups.values());
|
|
2019
|
+
}
|
|
2020
|
+
function applyPostFilters(posts, { categorySlug, articleSlug }) {
|
|
2021
|
+
let filtered = posts;
|
|
2022
|
+
if (categorySlug) {
|
|
2023
|
+
filtered = filtered.filter((post) => post.category?.slug === categorySlug);
|
|
2024
|
+
}
|
|
2025
|
+
if (articleSlug) {
|
|
2026
|
+
filtered = filtered.filter((post) => post.slug === articleSlug);
|
|
2027
|
+
}
|
|
2028
|
+
return filtered;
|
|
2029
|
+
}
|
|
2030
|
+
function splitFeaturedAndRest(posts) {
|
|
2031
|
+
const featured = posts.find((post) => post.featured) ?? null;
|
|
2032
|
+
const rest = featured ? posts.filter((post) => post.slug !== featured.slug) : [...posts];
|
|
2033
|
+
return { featured, rest };
|
|
2034
|
+
}
|
|
2035
|
+
function buildArticlesViewState(rawPosts, options) {
|
|
2036
|
+
const {
|
|
2037
|
+
categorySlug,
|
|
2038
|
+
articleSlug,
|
|
2039
|
+
searchQuery = "",
|
|
2040
|
+
groupPostsByCategory = defaultGroupPostsByCategory
|
|
2041
|
+
} = options;
|
|
2042
|
+
const posts = applyPostFilters(rawPosts, { categorySlug, articleSlug });
|
|
2043
|
+
const { featured, rest } = splitFeaturedAndRest(posts);
|
|
2044
|
+
const trimmedQuery = searchQuery.trim();
|
|
2045
|
+
const isSearching = trimmedQuery.length > 0;
|
|
2046
|
+
const categoryGroups = isSearching ? posts.length === 0 ? [] : [
|
|
2047
|
+
{
|
|
2048
|
+
categoryName: `Search results for "${trimmedQuery}"`,
|
|
2049
|
+
categorySlug: "search-results",
|
|
2050
|
+
posts
|
|
2051
|
+
}
|
|
2052
|
+
] : groupPostsByCategory(rest);
|
|
2053
|
+
return {
|
|
2054
|
+
posts,
|
|
2055
|
+
featured,
|
|
2056
|
+
rest,
|
|
2057
|
+
categoryGroups,
|
|
2058
|
+
isEmpty: !featured && rest.length === 0,
|
|
2059
|
+
isSearching,
|
|
2060
|
+
searchQuery: trimmedQuery
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
function renderArticlesListingBody(args) {
|
|
2064
|
+
const { state, loading, hasError, error, cmsImage: cmsImage2, searchNode, seoNode, jsonLdNode, renderProps } = args;
|
|
2065
|
+
const {
|
|
2066
|
+
eyebrow,
|
|
2067
|
+
title,
|
|
2068
|
+
description,
|
|
2069
|
+
renderRoot,
|
|
2070
|
+
renderHeader,
|
|
2071
|
+
renderFeaturedCard,
|
|
2072
|
+
renderPostCard,
|
|
2073
|
+
renderCategoryHeading,
|
|
2074
|
+
renderPostGrid,
|
|
2075
|
+
renderCategoryGroup,
|
|
2076
|
+
renderSkeleton,
|
|
2077
|
+
renderEmpty,
|
|
2078
|
+
renderContent
|
|
2079
|
+
} = renderProps;
|
|
2080
|
+
const headerNode = renderHeader ? renderHeader({ eyebrow, title, description, search: searchNode }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2081
|
+
eyebrow,
|
|
2082
|
+
title,
|
|
2083
|
+
description,
|
|
2084
|
+
searchNode
|
|
2085
|
+
] });
|
|
2086
|
+
const featuredNode = state.featured && !state.isSearching && renderFeaturedCard ? renderFeaturedCard({ post: state.featured, cmsImage: cmsImage2 }) : null;
|
|
2087
|
+
const noSearchResultsNode = state.isSearching && !loading && state.posts.length === 0 ? renderEmpty?.({ reason: "no-results", searchQuery: state.searchQuery }) ?? null : null;
|
|
2088
|
+
const groupsNode = noSearchResultsNode ? null : state.categoryGroups.map((group) => {
|
|
2089
|
+
const postCards = group.posts.map((post, index) => /* @__PURE__ */ jsx(Fragment, { children: renderPostCard({ post, index, group, cmsImage: cmsImage2 }) }, post.slug ?? index));
|
|
2090
|
+
const gridNode = renderPostGrid ? renderPostGrid({ posts: group.posts, group, children: postCards }) : /* @__PURE__ */ jsx(Fragment$1, { children: postCards });
|
|
2091
|
+
const headingNode = renderCategoryHeading ? renderCategoryHeading({ group }) : group.categoryName;
|
|
2092
|
+
const defaultContent = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2093
|
+
headingNode,
|
|
2094
|
+
gridNode
|
|
2095
|
+
] });
|
|
2096
|
+
return /* @__PURE__ */ jsx(Fragment, { children: renderCategoryGroup ? renderCategoryGroup({ group, defaultContent }) : defaultContent }, group.categorySlug);
|
|
2097
|
+
});
|
|
2098
|
+
const contentNode = !loading && !hasError && (state.featured || state.rest.length > 0) ? renderContent ? renderContent({ featured: featuredNode, groups: groupsNode, noSearchResults: noSearchResultsNode }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2099
|
+
featuredNode,
|
|
2100
|
+
noSearchResultsNode,
|
|
2101
|
+
groupsNode
|
|
2102
|
+
] }) : null;
|
|
2103
|
+
const body = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2104
|
+
seoNode,
|
|
2105
|
+
jsonLdNode,
|
|
2106
|
+
headerNode,
|
|
2107
|
+
loading ? renderSkeleton?.() : null,
|
|
2108
|
+
!loading && (hasError || state.isEmpty) ? renderEmpty?.({
|
|
2109
|
+
reason: hasError ? "error" : state.isSearching ? "no-results" : "no-posts",
|
|
2110
|
+
searchQuery: state.searchQuery,
|
|
2111
|
+
error
|
|
2112
|
+
}) : null,
|
|
2113
|
+
contentNode
|
|
2114
|
+
] });
|
|
2115
|
+
return renderRoot ? renderRoot({ children: body }) : /* @__PURE__ */ jsx(Fragment$1, { children: body });
|
|
2116
|
+
}
|
|
2117
|
+
function useDebouncedValue(value, delay) {
|
|
2118
|
+
const [debounced, setDebounced] = useState(value);
|
|
2119
|
+
useEffect(() => {
|
|
2120
|
+
const timer = setTimeout(() => setDebounced(value), delay);
|
|
2121
|
+
return () => clearTimeout(timer);
|
|
2122
|
+
}, [value, delay]);
|
|
2123
|
+
return debounced;
|
|
2124
|
+
}
|
|
2125
|
+
function useAsteroidArticlesState(props) {
|
|
2126
|
+
const { usePosts, categorySlug, articleSlug, searchDebounceMs = 800, groupPostsByCategory } = props;
|
|
2127
|
+
const [inputValue, setSearchQuery] = useState("");
|
|
2128
|
+
const debouncedSearchQuery = useDebouncedValue(inputValue, searchDebounceMs);
|
|
2129
|
+
const { posts: rawPosts, loading, error } = usePosts(debouncedSearchQuery);
|
|
2130
|
+
const view = useMemo(
|
|
2131
|
+
() => buildArticlesViewState(rawPosts, {
|
|
2132
|
+
categorySlug,
|
|
2133
|
+
articleSlug,
|
|
2134
|
+
searchQuery: debouncedSearchQuery,
|
|
2135
|
+
groupPostsByCategory
|
|
2136
|
+
}),
|
|
2137
|
+
[rawPosts, categorySlug, articleSlug, debouncedSearchQuery, groupPostsByCategory]
|
|
2138
|
+
);
|
|
2139
|
+
return {
|
|
2140
|
+
...view,
|
|
2141
|
+
loading,
|
|
2142
|
+
error,
|
|
2143
|
+
hasError: Boolean(error),
|
|
2144
|
+
inputValue,
|
|
2145
|
+
setSearchQuery
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
function AsteroidArticlesListing(props) {
|
|
2149
|
+
const { seo, categorySlug, noindex, renderSearch, renderJsonLd, children, ...renderProps } = props;
|
|
2150
|
+
const state = useAsteroidArticlesState(props);
|
|
2151
|
+
const cmsImage2 = useCmsImage();
|
|
2152
|
+
if (children) return /* @__PURE__ */ jsx(Fragment$1, { children: children(state) });
|
|
2153
|
+
const categoryName = categorySlug ? state.posts[0]?.category?.name?.trim() : void 0;
|
|
2154
|
+
const seoNode = seo ? /* @__PURE__ */ jsx(
|
|
2155
|
+
Seo,
|
|
2156
|
+
{
|
|
2157
|
+
...seoValuesToClientProps(
|
|
2158
|
+
buildArticleListingSeoValues(seo, { categoryName, categorySlug, noindex })
|
|
2159
|
+
)
|
|
2160
|
+
}
|
|
2161
|
+
) : null;
|
|
2162
|
+
const jsonLdNode = seo ? renderJsonLd?.(state) ?? /* @__PURE__ */ jsx(
|
|
2163
|
+
JsonLd,
|
|
2164
|
+
{
|
|
2165
|
+
data: buildCollectionJsonLd({
|
|
2166
|
+
name: categoryName || `${seo.siteName} ${seo.contentLabel ?? "Articles"}`,
|
|
2167
|
+
description: seo.defaultDescription || "",
|
|
2168
|
+
url: `${(seo.baseUrl || "").replace(/\/$/, "")}${seo.articlePath ?? "/blog"}${categorySlug ? `/category/${categorySlug}` : ""}`,
|
|
2169
|
+
siteUrl: (seo.baseUrl || "").replace(/\/$/, "")
|
|
2170
|
+
})
|
|
2171
|
+
}
|
|
2172
|
+
) : null;
|
|
2173
|
+
const searchNode = renderSearch ? renderSearch({
|
|
2174
|
+
value: state.inputValue,
|
|
2175
|
+
onChange: state.setSearchQuery,
|
|
2176
|
+
onSubmit: (event) => event.preventDefault()
|
|
2177
|
+
}) : null;
|
|
2178
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: renderArticlesListingBody({
|
|
2179
|
+
state,
|
|
2180
|
+
loading: state.loading,
|
|
2181
|
+
hasError: state.hasError,
|
|
2182
|
+
error: state.error,
|
|
2183
|
+
cmsImage: cmsImage2,
|
|
2184
|
+
searchNode,
|
|
2185
|
+
seoNode,
|
|
2186
|
+
jsonLdNode,
|
|
2187
|
+
renderProps
|
|
2188
|
+
}) });
|
|
2189
|
+
}
|
|
2190
|
+
function ArticleSearchBox({
|
|
2191
|
+
paramKey = "q",
|
|
2192
|
+
placeholder = "Search articles...",
|
|
2193
|
+
debounceMs = 500,
|
|
2194
|
+
className,
|
|
2195
|
+
render
|
|
2196
|
+
}) {
|
|
2197
|
+
const router = useRouter();
|
|
2198
|
+
const pathname = usePathname();
|
|
2199
|
+
const searchParams = useSearchParams();
|
|
2200
|
+
const initial = searchParams.get(paramKey) ?? "";
|
|
2201
|
+
const [value, setValue] = useState(initial);
|
|
2202
|
+
const searchParamsRef = useRef(searchParams);
|
|
2203
|
+
searchParamsRef.current = searchParams;
|
|
2204
|
+
useEffect(() => {
|
|
2205
|
+
const timer = setTimeout(() => {
|
|
2206
|
+
const params = new URLSearchParams(Array.from(searchParamsRef.current.entries()));
|
|
2207
|
+
const trimmed = value.trim();
|
|
2208
|
+
if (trimmed) params.set(paramKey, trimmed);
|
|
2209
|
+
else params.delete(paramKey);
|
|
2210
|
+
const query = params.toString();
|
|
2211
|
+
router.replace(query ? `${pathname}?${query}` : pathname, { scroll: false });
|
|
2212
|
+
}, debounceMs);
|
|
2213
|
+
return () => clearTimeout(timer);
|
|
2214
|
+
}, [value, debounceMs, paramKey, pathname]);
|
|
2215
|
+
const onSubmit = (event) => event.preventDefault();
|
|
2216
|
+
if (render) return /* @__PURE__ */ jsx(Fragment$1, { children: render({ value, onChange: setValue, onSubmit }) });
|
|
2217
|
+
return /* @__PURE__ */ jsx("form", { onSubmit, className, children: /* @__PURE__ */ jsx(
|
|
2218
|
+
"input",
|
|
2219
|
+
{
|
|
2220
|
+
type: "search",
|
|
2221
|
+
value,
|
|
2222
|
+
placeholder,
|
|
2223
|
+
"aria-label": placeholder,
|
|
2224
|
+
onChange: (event) => setValue(event.target.value)
|
|
2225
|
+
}
|
|
2226
|
+
) });
|
|
2227
|
+
}
|
|
1731
2228
|
|
|
1732
|
-
export { AsteroidCMSProvider, RichTextContent, extractHeadingsFromElement, extractHeadingsFromHtml, slugify, useAsteroidCMSConfig, useCmsContent, useCmsImage, useCmsMutate };
|
|
2229
|
+
export { ArticleSearchBox, AsteroidArticlePage, AsteroidArticlesListing, AsteroidCMSProvider, JsonLd, RichTextContent, Seo, defaultGetCategoryName, defaultGroupPostsByCategory, extractHeadingsFromElement, extractHeadingsFromHtml, slugify, useAsteroidArticlesState, useAsteroidCMSConfig, useCmsContent, useCmsImage, useCmsMutate };
|
|
1733
2230
|
//# sourceMappingURL=client.js.map
|
|
1734
2231
|
//# sourceMappingURL=client.js.map
|