@shipsite.dev/components 0.2.30 → 0.2.32
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/dist/blog/BlogArticle.d.ts +11 -1
- package/dist/blog/BlogArticle.d.ts.map +1 -1
- package/dist/blog/BlogArticle.js +2 -2
- package/dist/blog/BlogArticle.js.map +1 -1
- package/dist/blog/BlogArticleClient.d.ts +11 -1
- package/dist/blog/BlogArticleClient.d.ts.map +1 -1
- package/dist/blog/BlogArticleClient.js +70 -40
- package/dist/blog/BlogArticleClient.js.map +1 -1
- package/package.json +1 -1
- package/src/blog/BlogArticle.tsx +41 -2
- package/src/blog/BlogArticleClient.tsx +294 -75
|
@@ -3,15 +3,25 @@ interface BlogArticleAuthor {
|
|
|
3
3
|
name: string;
|
|
4
4
|
role: string;
|
|
5
5
|
image: string;
|
|
6
|
+
bio?: string;
|
|
6
7
|
}
|
|
7
8
|
interface BlogArticleProps {
|
|
8
9
|
id?: string;
|
|
9
10
|
children: React.ReactNode;
|
|
10
11
|
contentFolder?: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
heroImage?: string;
|
|
14
|
+
blogHref?: string;
|
|
11
15
|
date?: string;
|
|
12
16
|
readingTime?: number;
|
|
13
17
|
author?: BlogArticleAuthor;
|
|
18
|
+
blogLabel?: string;
|
|
19
|
+
tocLabel?: string;
|
|
20
|
+
shareLabel?: string;
|
|
21
|
+
copyLinkLabel?: string;
|
|
22
|
+
copiedLabel?: string;
|
|
23
|
+
aboutAuthorLabel?: string;
|
|
14
24
|
}
|
|
15
|
-
export declare function BlogArticle({ id, children, author, date, readingTime }: BlogArticleProps): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export declare function BlogArticle({ id, children, title, heroImage, blogHref, author, date, readingTime, blogLabel, tocLabel, shareLabel, copyLinkLabel, copiedLabel, aboutAuthorLabel, }: BlogArticleProps): import("react/jsx-runtime").JSX.Element;
|
|
16
26
|
export {};
|
|
17
27
|
//# sourceMappingURL=BlogArticle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlogArticle.d.ts","sourceRoot":"","sources":["../../src/blog/BlogArticle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"BlogArticle.d.ts","sourceRoot":"","sources":["../../src/blog/BlogArticle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,UAAU,gBAAgB;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,WAAW,CAAC,EAC1B,EAAE,EACF,QAAQ,EACR,KAAK,EACL,SAAS,EACT,QAAQ,EACR,MAAM,EACN,IAAI,EACJ,WAAW,EACX,SAAS,EACT,QAAQ,EACR,UAAU,EACV,aAAa,EACb,WAAW,EACX,gBAAgB,GACjB,EAAE,gBAAgB,2CAoBlB"}
|
package/dist/blog/BlogArticle.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { BlogArticleClient } from './BlogArticleClient';
|
|
3
|
-
export function BlogArticle({ id, children, author, date, readingTime }) {
|
|
4
|
-
return (_jsx(BlogArticleClient, { id: id, author: author, date: date, readingTime: readingTime, children: children }));
|
|
3
|
+
export function BlogArticle({ id, children, title, heroImage, blogHref, author, date, readingTime, blogLabel, tocLabel, shareLabel, copyLinkLabel, copiedLabel, aboutAuthorLabel, }) {
|
|
4
|
+
return (_jsx(BlogArticleClient, { id: id, title: title, heroImage: heroImage, blogHref: blogHref, author: author, date: date, readingTime: readingTime, blogLabel: blogLabel, tocLabel: tocLabel, shareLabel: shareLabel, copyLinkLabel: copyLinkLabel, copiedLabel: copiedLabel, aboutAuthorLabel: aboutAuthorLabel, children: children }));
|
|
5
5
|
}
|
|
6
6
|
//# sourceMappingURL=BlogArticle.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlogArticle.js","sourceRoot":"","sources":["../../src/blog/BlogArticle.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"BlogArticle.js","sourceRoot":"","sources":["../../src/blog/BlogArticle.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AA2BxD,MAAM,UAAU,WAAW,CAAC,EAC1B,EAAE,EACF,QAAQ,EACR,KAAK,EACL,SAAS,EACT,QAAQ,EACR,MAAM,EACN,IAAI,EACJ,WAAW,EACX,SAAS,EACT,QAAQ,EACR,UAAU,EACV,aAAa,EACb,WAAW,EACX,gBAAgB,GACC;IACjB,OAAO,CACL,KAAC,iBAAiB,IAChB,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,IAAI,EACV,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,aAAa,EAC5B,WAAW,EAAE,WAAW,EACxB,gBAAgB,EAAE,gBAAgB,YAEjC,QAAQ,GACS,CACrB,CAAC;AACJ,CAAC"}
|
|
@@ -4,14 +4,24 @@ interface BlogArticleAuthor {
|
|
|
4
4
|
name: string;
|
|
5
5
|
role: string;
|
|
6
6
|
image: ImageSource;
|
|
7
|
+
bio?: string;
|
|
7
8
|
}
|
|
8
9
|
interface BlogArticleClientProps {
|
|
9
10
|
id?: string;
|
|
10
11
|
children: React.ReactNode;
|
|
12
|
+
title?: string;
|
|
13
|
+
heroImage?: ImageSource;
|
|
14
|
+
blogHref?: string;
|
|
11
15
|
author?: BlogArticleAuthor;
|
|
12
16
|
date?: string;
|
|
13
17
|
readingTime?: number;
|
|
18
|
+
blogLabel?: string;
|
|
19
|
+
tocLabel?: string;
|
|
20
|
+
shareLabel?: string;
|
|
21
|
+
copyLinkLabel?: string;
|
|
22
|
+
copiedLabel?: string;
|
|
23
|
+
aboutAuthorLabel?: string;
|
|
14
24
|
}
|
|
15
|
-
export declare function BlogArticleClient({ id, children, author, date, readingTime, }: BlogArticleClientProps): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export declare function BlogArticleClient({ id, children, title, heroImage, blogHref, author, date, readingTime, blogLabel, tocLabel, shareLabel, copyLinkLabel, copiedLabel, aboutAuthorLabel, }: BlogArticleClientProps): import("react/jsx-runtime").JSX.Element;
|
|
16
26
|
export {};
|
|
17
27
|
//# sourceMappingURL=BlogArticleClient.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlogArticleClient.d.ts","sourceRoot":"","sources":["../../src/blog/BlogArticleClient.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmD,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"BlogArticleClient.d.ts","sourceRoot":"","sources":["../../src/blog/BlogArticleClient.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAGxE,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAoEjE,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,UAAU,sBAAsB;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAiDD,wBAAgB,iBAAiB,CAAC,EAChC,EAAE,EACF,QAAQ,EACR,KAAK,EACL,SAAS,EACT,QAAkB,EAClB,MAAM,EACN,IAAI,EACJ,WAAW,EACX,SAAkB,EAClB,QAA4B,EAC5B,UAA4B,EAC5B,aAA2B,EAC3B,WAAsB,EACtB,gBAAqC,GACtC,EAAE,sBAAsB,2CAkRxB"}
|
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
4
|
+
import { Home, ChevronRight } from 'lucide-react';
|
|
4
5
|
import { cn } from '../lib/utils';
|
|
5
6
|
import { ThemeImage } from '../ui/theme-image';
|
|
6
|
-
|
|
7
|
+
// --- Share Icons (inline SVGs for brand icons) ---
|
|
8
|
+
const LinkedInIcon = () => (_jsx("svg", { width: "100%", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M22.2234 0H1.77187C0.792187 0 0 0.773438 0 1.72969V22.2656C0 23.2219 0.792187 24 1.77187 24H22.2234C23.2031 24 24 23.2219 24 22.2703V1.72969C24 0.773438 23.2031 0 22.2234 0ZM7.12031 20.4516H3.55781V8.99531H7.12031V20.4516ZM5.33906 7.43438C4.19531 7.43438 3.27188 6.51094 3.27188 5.37187C3.27188 4.23281 4.19531 3.30937 5.33906 3.30937C6.47813 3.30937 7.40156 4.23281 7.40156 5.37187C7.40156 6.50625 6.47813 7.43438 5.33906 7.43438ZM20.4516 20.4516H16.8937V14.8828C16.8937 13.5562 16.8703 11.8453 15.0422 11.8453C13.1906 11.8453 12.9094 13.2937 12.9094 14.7891V20.4516H9.35625V8.99531H12.7687V10.5609H12.8156C13.2891 9.66094 14.4516 8.70938 16.1813 8.70938C19.7859 8.70938 20.4516 11.0813 20.4516 14.1656V20.4516Z", fill: "currentColor" }) }));
|
|
9
|
+
const FacebookIcon = () => (_jsx("svg", { width: "100%", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 17.9895 4.3882 22.954 10.125 23.8542V15.4688H7.07812V12H10.125V9.35625C10.125 6.34875 11.9166 4.6875 14.6576 4.6875C15.9701 4.6875 17.3438 4.92188 17.3438 4.92188V7.875H15.8306C14.34 7.875 13.875 8.80008 13.875 9.75V12H17.2031L16.6711 15.4688H13.875V23.8542C19.6118 22.954 24 17.9895 24 12Z", fill: "currentColor" }) }));
|
|
10
|
+
const XIcon = () => (_jsx("svg", { width: "100%", viewBox: "0 0 20 20", fill: "none", children: _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M13.1385 18.75L8.72419 12.458L3.19803 18.75H0.860107L7.68695 10.9793L0.860107 1.25H6.86257L11.023 7.18013L16.2358 1.25H18.5738L12.0637 8.66084L19.141 18.75H13.1385ZM15.742 16.9762H14.1681L4.20766 3.02386H5.78185L9.77107 8.61047L10.4609 9.57989L15.742 16.9762Z", fill: "currentColor" }) }));
|
|
11
|
+
const RedditIcon = () => (_jsxs("svg", { width: "100%", viewBox: "0 0 20 20", fill: "none", children: [_jsx("path", { d: "M7.70781 9.99998C7.13471 9.99998 6.66687 10.4678 6.66687 11.0409C6.66687 11.614 7.13471 12.0819 7.70781 12.0819C8.28091 12.0819 8.74875 11.614 8.74875 11.0409C8.74875 10.4678 8.28091 9.99998 7.70781 9.99998Z", fill: "currentColor" }), _jsx("path", { d: "M10.0119 14.5497C10.4096 14.5497 11.7663 14.5029 12.4797 13.7895C12.585 13.6842 12.585 13.5204 12.5031 13.4035C12.3979 13.2982 12.2224 13.2982 12.1172 13.4035C11.661 13.8479 10.7137 14.0117 10.0236 14.0117C9.33354 14.0117 8.37448 13.8479 7.93003 13.4035C7.82477 13.2982 7.64933 13.2982 7.54407 13.4035C7.4388 13.5088 7.4388 13.6842 7.54407 13.7895C8.24582 14.4912 9.61424 14.5497 10.0119 14.5497Z", fill: "currentColor" }), _jsx("path", { d: "M11.2517 11.0409C11.2517 11.614 11.7195 12.0819 12.2926 12.0819C12.8657 12.0819 13.3335 11.614 13.3335 11.0409C13.3335 10.4678 12.8657 9.99998 12.2926 9.99998C11.7195 9.99998 11.2517 10.4678 11.2517 11.0409Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M20 10C20 15.5228 15.5228 20 10 20C4.47715 20 0 15.5228 0 10C0 4.47715 4.47715 0 10 0C15.5228 0 20 4.47715 20 10ZM15.2049 8.53799C16.0119 8.53799 16.6669 9.19296 16.6669 9.99998C16.6669 10.5965 16.3043 11.1111 15.8248 11.345C15.8482 11.4854 15.8599 11.6257 15.8599 11.7778C15.8599 14.0234 13.2517 15.8362 10.0236 15.8362C6.79553 15.8362 4.18734 14.0234 4.18734 11.7778C4.18734 11.6257 4.19904 11.4737 4.22243 11.3333C3.70781 11.0994 3.35693 10.5965 3.35693 9.99998C3.35693 9.19296 4.0119 8.53799 4.81892 8.53799C5.20489 8.53799 5.56746 8.70173 5.82477 8.94735C6.83062 8.21051 8.22243 7.75437 9.77799 7.70758L10.5148 4.2222C10.5382 4.15203 10.5733 4.09355 10.6318 4.05846C10.6903 4.02337 10.7604 4.01168 10.8306 4.02337L13.2517 4.53799C13.4154 4.18711 13.7663 3.9532 14.1756 3.9532C14.7487 3.9532 15.2166 4.42103 15.2166 4.99413C15.2166 5.56723 14.7487 6.03507 14.1756 6.03507C13.6142 6.03507 13.1581 5.59062 13.1347 5.04092L10.971 4.58478L10.3043 7.70758C11.8248 7.76606 13.2049 8.2339 14.199 8.94735C14.4563 8.69004 14.8072 8.53799 15.2049 8.53799Z", fill: "currentColor" })] }));
|
|
12
|
+
const CopyIcon = () => (_jsx("svg", { width: "100%", viewBox: "0 0 20 20", fill: "none", children: _jsx("path", { d: "M8.33326 10.8333C8.69113 11.3118 9.14772 11.7077 9.67205 11.9941C10.1964 12.2806 10.7762 12.4509 11.3721 12.4936C11.9681 12.5363 12.5662 12.4503 13.126 12.2415C13.6858 12.0327 14.1942 11.7059 14.6166 11.2833L17.1166 8.78334C17.8756 7.9975 18.2956 6.94499 18.2861 5.85251C18.2766 4.76002 17.8384 3.71497 17.0658 2.94243C16.2933 2.1699 15.2482 1.7317 14.1558 1.7222C13.0633 1.71271 12.0108 2.13269 11.2249 2.89168L9.79159 4.31668M11.6666 9.16668C11.3087 8.68824 10.8521 8.29236 10.3278 8.00589C9.80347 7.71943 9.22367 7.54908 8.62771 7.5064C8.03176 7.46372 7.4336 7.54971 6.8738 7.75853C6.314 7.96735 5.80566 8.29412 5.38326 8.71668L2.88326 11.2167C2.12426 12.0025 1.70429 13.055 1.71378 14.1475C1.72327 15.24 2.16148 16.2851 2.93401 17.0576C3.70655 17.8301 4.7516 18.2683 5.84408 18.2778C6.93657 18.2873 7.98908 17.8673 8.77492 17.1083L10.1999 15.6833", stroke: "currentColor", strokeWidth: "1.66667", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
13
|
+
const CheckIcon = () => (_jsx("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", children: _jsx("path", { d: "M15 4.5L6.75 12.75L3 9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
14
|
+
// --- Share Buttons ---
|
|
15
|
+
const shareBtnClass = 'w-[2.625rem] h-[2.625rem] flex items-center justify-center border rounded-lg border-border text-muted-foreground hover:bg-muted/50 hover:text-foreground transition-colors p-2.5 cursor-pointer';
|
|
16
|
+
function ShareButtons({ onCopyLink, onShareLinkedIn, onShareFacebook, onShareX, onShareReddit, copyLabel, copied, copiedLabel, }) {
|
|
17
|
+
return (_jsxs("div", { className: "flex flex-wrap gap-3", children: [_jsx("button", { onClick: onCopyLink, className: shareBtnClass, "aria-label": copyLabel, title: copied ? copiedLabel : copyLabel, children: copied ? _jsx(CheckIcon, {}) : _jsx(CopyIcon, {}) }), _jsx("button", { onClick: onShareLinkedIn, className: shareBtnClass, "aria-label": "Share on LinkedIn", children: _jsx(LinkedInIcon, {}) }), _jsx("button", { onClick: onShareFacebook, className: shareBtnClass, "aria-label": "Share on Facebook", children: _jsx(FacebookIcon, {}) }), _jsx("button", { onClick: onShareX, className: shareBtnClass, "aria-label": "Share on X", children: _jsx(XIcon, {}) }), _jsx("button", { onClick: onShareReddit, className: shareBtnClass, "aria-label": "Share on Reddit", children: _jsx(RedditIcon, {}) })] }));
|
|
18
|
+
}
|
|
19
|
+
// --- Main Component ---
|
|
20
|
+
export function BlogArticleClient({ id, children, title, heroImage, blogHref = '/blog', author, date, readingTime, blogLabel = 'Blog', tocLabel = 'In this article', shareLabel = 'Share article', copyLinkLabel = 'Copy link', copiedLabel = 'Copied', aboutAuthorLabel = 'About the Author', }) {
|
|
7
21
|
const contentRef = useRef(null);
|
|
8
22
|
const [tocItems, setTocItems] = useState([]);
|
|
9
23
|
const [activeId, setActiveId] = useState('');
|
|
10
24
|
const [activeH2Id, setActiveH2Id] = useState('');
|
|
25
|
+
const [copied, setCopied] = useState(false);
|
|
11
26
|
// Extract headings from rendered content
|
|
12
27
|
useEffect(() => {
|
|
13
28
|
const el = contentRef.current;
|
|
@@ -17,21 +32,17 @@ export function BlogArticleClient({ id, children, author, date, readingTime, })
|
|
|
17
32
|
const headings = el.querySelectorAll('h2, h3');
|
|
18
33
|
const items = [];
|
|
19
34
|
headings.forEach((heading) => {
|
|
20
|
-
const
|
|
21
|
-
if (!
|
|
35
|
+
const text = heading.textContent?.trim() || '';
|
|
36
|
+
if (!text || heading.hasAttribute('data-toc-exclude'))
|
|
22
37
|
return;
|
|
23
38
|
if (!heading.id) {
|
|
24
39
|
heading.id =
|
|
25
|
-
|
|
40
|
+
text
|
|
26
41
|
.toLowerCase()
|
|
27
42
|
.replace(/[^a-z0-9]+/g, '-')
|
|
28
43
|
.replace(/(^-|-$)/g, '') || '';
|
|
29
44
|
}
|
|
30
|
-
items.push({
|
|
31
|
-
id: heading.id,
|
|
32
|
-
title,
|
|
33
|
-
level: heading.tagName === 'H2' ? 2 : 3,
|
|
34
|
-
});
|
|
45
|
+
items.push({ id: heading.id, title: text, level: heading.tagName === 'H2' ? 2 : 3 });
|
|
35
46
|
});
|
|
36
47
|
setTocItems(items);
|
|
37
48
|
});
|
|
@@ -45,11 +56,11 @@ export function BlogArticleClient({ id, children, author, date, readingTime, })
|
|
|
45
56
|
const observer = new IntersectionObserver((entries) => {
|
|
46
57
|
for (const entry of entries) {
|
|
47
58
|
if (entry.isIntersecting) {
|
|
48
|
-
const
|
|
49
|
-
setActiveId(
|
|
50
|
-
const tocItem = tocItems.find((t) => t.id ===
|
|
59
|
+
const entryId = entry.target.id;
|
|
60
|
+
setActiveId(entryId);
|
|
61
|
+
const tocItem = tocItems.find((t) => t.id === entryId);
|
|
51
62
|
if (tocItem?.level === 2) {
|
|
52
|
-
setActiveH2Id(
|
|
63
|
+
setActiveH2Id(entryId);
|
|
53
64
|
}
|
|
54
65
|
else if (tocItem?.level === 3) {
|
|
55
66
|
const idx = tocItems.indexOf(tocItem);
|
|
@@ -70,38 +81,57 @@ export function BlogArticleClient({ id, children, author, date, readingTime, })
|
|
|
70
81
|
});
|
|
71
82
|
return () => observer.disconnect();
|
|
72
83
|
}, [tocItems]);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const el = document.getElementById(id);
|
|
84
|
+
const scrollToHeading = useCallback((headingId) => {
|
|
85
|
+
const el = document.getElementById(headingId);
|
|
76
86
|
if (el) {
|
|
77
87
|
const top = el.getBoundingClientRect().top + window.scrollY - 100;
|
|
78
88
|
window.scrollTo({ top, behavior: 'smooth' });
|
|
79
89
|
}
|
|
80
90
|
}, []);
|
|
91
|
+
// Social sharing
|
|
92
|
+
const shareUrl = typeof window !== 'undefined' ? window.location.href : '';
|
|
93
|
+
const shareTitle = title || '';
|
|
94
|
+
const openShare = (url) => window.open(url, '_blank', 'noopener,noreferrer,width=600,height=600');
|
|
95
|
+
const shareLinkedIn = () => openShare(`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(shareUrl)}`);
|
|
96
|
+
const shareFacebook = () => openShare(`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareUrl)}`);
|
|
97
|
+
const shareX = () => openShare(`https://twitter.com/intent/tweet?url=${encodeURIComponent(shareUrl)}&text=${encodeURIComponent(shareTitle)}`);
|
|
98
|
+
const shareReddit = () => openShare(`https://reddit.com/submit?url=${encodeURIComponent(shareUrl)}&title=${encodeURIComponent(shareTitle)}`);
|
|
99
|
+
const copyLink = async () => {
|
|
100
|
+
try {
|
|
101
|
+
await navigator.clipboard.writeText(shareUrl);
|
|
102
|
+
setCopied(true);
|
|
103
|
+
setTimeout(() => setCopied(false), 2000);
|
|
104
|
+
}
|
|
105
|
+
catch { /* noop */ }
|
|
106
|
+
};
|
|
81
107
|
const formattedDate = date
|
|
82
|
-
? new Date(date + 'T00:00:00').toLocaleDateString(undefined, {
|
|
83
|
-
year: 'numeric',
|
|
84
|
-
month: 'long',
|
|
85
|
-
day: 'numeric',
|
|
86
|
-
})
|
|
108
|
+
? new Date(date + 'T00:00:00').toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })
|
|
87
109
|
: '';
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
110
|
+
const shareProps = {
|
|
111
|
+
onCopyLink: copyLink,
|
|
112
|
+
onShareLinkedIn: shareLinkedIn,
|
|
113
|
+
onShareFacebook: shareFacebook,
|
|
114
|
+
onShareX: shareX,
|
|
115
|
+
onShareReddit: shareReddit,
|
|
116
|
+
copyLabel: copyLinkLabel,
|
|
117
|
+
copied,
|
|
118
|
+
copiedLabel,
|
|
119
|
+
};
|
|
120
|
+
return (_jsxs("article", { id: id, className: "pt-16", children: [_jsxs("div", { className: "mx-auto w-full max-w-[76rem] px-[clamp(1rem,3vw,3rem)] flex flex-col gap-8", children: [_jsx("nav", { "aria-label": "Breadcrumb", children: _jsxs("ul", { className: "flex flex-wrap items-center gap-2", children: [_jsx("li", { children: _jsx("a", { href: "/", className: "text-sm text-muted-foreground hover:text-foreground flex items-center", "aria-label": "Home", children: _jsx(Home, { className: "w-5 h-5" }) }) }), _jsx("li", { className: "flex items-center", children: _jsx(ChevronRight, { className: "w-4 h-4 text-muted-foreground/60" }) }), _jsx("li", { children: _jsx("a", { href: blogHref, className: "text-sm font-semibold text-muted-foreground hover:text-foreground", children: blogLabel }) }), title && (_jsxs(_Fragment, { children: [_jsx("li", { className: "flex items-center", children: _jsx(ChevronRight, { className: "w-4 h-4 text-foreground" }) }), _jsx("li", { children: _jsx("span", { className: "text-sm font-semibold text-muted-foreground", children: title }) })] }))] }) }), title && (_jsx("h1", { className: "text-[clamp(2rem,1.7rem+1.5vw,3rem)] leading-[1.3] font-semibold tracking-[-0.02em] text-foreground", children: title })), (formattedDate || readingTime) && (_jsxs("div", { className: "flex items-center gap-3", children: [formattedDate && _jsx("span", { className: "text-base text-foreground", children: formattedDate }), formattedDate && readingTime && readingTime > 0 && _jsx("span", { className: "w-px h-3.5 bg-border" }), readingTime && readingTime > 0 && _jsxs("span", { className: "text-base text-foreground", children: [readingTime, " min read"] })] }))] }), _jsxs("div", { className: "mx-auto w-full max-w-[76rem] px-[clamp(1rem,3vw,3rem)] border-b border-border", children: [_jsx("div", { className: "h-[clamp(2.5rem,2rem+2.5vw,4rem)]" }), _jsxs("div", { className: "flex flex-col lg:flex-row gap-[clamp(2rem,1.7rem+1.5vw,3rem)]", children: [_jsxs("div", { className: "min-w-0 flex-1 max-w-[53.25rem]", ref: contentRef, children: [heroImage && (_jsx(ThemeImage, { src: heroImage, alt: title || 'Blog article', className: "w-full h-auto object-cover mb-10 rounded-2xl" })), _jsx("div", { className: "blog-content", children: children }), author && (_jsxs("section", { className: "bg-muted p-8 rounded-2xl flex flex-col gap-5 mt-12", children: [_jsx("p", { className: "text-lg font-semibold text-foreground", children: aboutAuthorLabel }), _jsxs("div", { className: "flex items-center gap-4", children: [author.image && (_jsx(ThemeImage, { src: author.image, alt: author.name, className: "w-[clamp(2.75rem,2.1rem+3vw,4.5rem)] h-[clamp(2.75rem,2.1rem+3vw,4.5rem)] rounded-full object-cover" })), _jsxs("div", { children: [_jsx("p", { className: "font-medium text-foreground", children: author.name }), author.role && _jsx("p", { className: "text-sm text-muted-foreground", children: author.role })] })] }), author.bio && _jsx("p", { className: "text-base text-muted-foreground leading-normal", children: author.bio })] }))] }), _jsx("aside", { className: "hidden lg:block w-full max-w-[16rem] xl:max-w-[19.75rem] shrink-0", children: _jsxs("div", { className: "sticky top-24 flex flex-col gap-5", children: [tocItems.length > 0 && (_jsxs("div", { children: [_jsx("p", { className: "font-medium text-foreground mb-4", children: tocLabel }), _jsx("ul", { className: "flex flex-col gap-2", children: tocItems.map((item) => {
|
|
121
|
+
const isActive = activeId === item.id;
|
|
122
|
+
if (item.level === 3) {
|
|
123
|
+
const idx = tocItems.indexOf(item);
|
|
124
|
+
let parentH2Id = '';
|
|
125
|
+
for (let i = idx - 1; i >= 0; i--) {
|
|
126
|
+
if (tocItems[i].level === 2) {
|
|
127
|
+
parentH2Id = tocItems[i].id;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (parentH2Id !== activeH2Id)
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return (_jsx("li", { className: item.level === 3 ? 'pl-3' : '', children: _jsxs("button", { onClick: () => scrollToHeading(item.id), className: cn('flex items-center gap-2 w-full cursor-pointer text-left', isActive ? 'text-foreground font-medium' : 'text-muted-foreground hover:text-foreground'), children: [_jsx("span", { className: cn('w-3 h-px shrink-0 transition-colors', isActive ? 'bg-foreground' : 'bg-muted-foreground/40') }), _jsx("span", { className: "text-sm leading-snug line-clamp-1", children: item.title })] }) }, item.id));
|
|
135
|
+
}) })] })), tocItems.length > 0 && _jsx("hr", { className: "border-border" }), author && (_jsxs("div", { children: [author.image && (_jsx(ThemeImage, { src: author.image, alt: author.name, className: "w-14 h-14 rounded-full object-cover mb-4" })), _jsx("p", { className: "text-base font-medium text-foreground", children: author.name }), author.role && _jsx("p", { className: "text-sm text-muted-foreground", children: author.role })] })), author && _jsx("hr", { className: "border-border" }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsx("p", { className: "text-base font-medium text-foreground", children: shareLabel }), _jsx(ShareButtons, { ...shareProps })] })] }) })] }), _jsx("div", { className: "h-[clamp(2.5rem,2rem+2.5vw,4rem)]" })] }), _jsx("div", { className: "lg:hidden mx-auto w-full max-w-[76rem] px-[clamp(1rem,3vw,3rem)] mt-8 mb-8", children: _jsxs("div", { className: "flex flex-col gap-4", children: [_jsx("p", { className: "text-base font-medium text-foreground", children: shareLabel }), _jsx(ShareButtons, { ...shareProps })] }) })] }));
|
|
106
136
|
}
|
|
107
137
|
//# sourceMappingURL=BlogArticleClient.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlogArticleClient.js","sourceRoot":"","sources":["../../src/blog/BlogArticleClient.tsx"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BlogArticleClient.js","sourceRoot":"","sources":["../../src/blog/BlogArticleClient.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/blog/BlogArticle.tsx
CHANGED
|
@@ -5,20 +5,59 @@ interface BlogArticleAuthor {
|
|
|
5
5
|
name: string;
|
|
6
6
|
role: string;
|
|
7
7
|
image: string;
|
|
8
|
+
bio?: string;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
interface BlogArticleProps {
|
|
11
12
|
id?: string;
|
|
12
13
|
children: React.ReactNode;
|
|
13
14
|
contentFolder?: string;
|
|
15
|
+
title?: string;
|
|
16
|
+
heroImage?: string;
|
|
17
|
+
blogHref?: string;
|
|
14
18
|
date?: string;
|
|
15
19
|
readingTime?: number;
|
|
16
20
|
author?: BlogArticleAuthor;
|
|
21
|
+
blogLabel?: string;
|
|
22
|
+
tocLabel?: string;
|
|
23
|
+
shareLabel?: string;
|
|
24
|
+
copyLinkLabel?: string;
|
|
25
|
+
copiedLabel?: string;
|
|
26
|
+
aboutAuthorLabel?: string;
|
|
17
27
|
}
|
|
18
28
|
|
|
19
|
-
export function BlogArticle({
|
|
29
|
+
export function BlogArticle({
|
|
30
|
+
id,
|
|
31
|
+
children,
|
|
32
|
+
title,
|
|
33
|
+
heroImage,
|
|
34
|
+
blogHref,
|
|
35
|
+
author,
|
|
36
|
+
date,
|
|
37
|
+
readingTime,
|
|
38
|
+
blogLabel,
|
|
39
|
+
tocLabel,
|
|
40
|
+
shareLabel,
|
|
41
|
+
copyLinkLabel,
|
|
42
|
+
copiedLabel,
|
|
43
|
+
aboutAuthorLabel,
|
|
44
|
+
}: BlogArticleProps) {
|
|
20
45
|
return (
|
|
21
|
-
<BlogArticleClient
|
|
46
|
+
<BlogArticleClient
|
|
47
|
+
id={id}
|
|
48
|
+
title={title}
|
|
49
|
+
heroImage={heroImage}
|
|
50
|
+
blogHref={blogHref}
|
|
51
|
+
author={author}
|
|
52
|
+
date={date}
|
|
53
|
+
readingTime={readingTime}
|
|
54
|
+
blogLabel={blogLabel}
|
|
55
|
+
tocLabel={tocLabel}
|
|
56
|
+
shareLabel={shareLabel}
|
|
57
|
+
copyLinkLabel={copyLinkLabel}
|
|
58
|
+
copiedLabel={copiedLabel}
|
|
59
|
+
aboutAuthorLabel={aboutAuthorLabel}
|
|
60
|
+
>
|
|
22
61
|
{children}
|
|
23
62
|
</BlogArticleClient>
|
|
24
63
|
);
|
|
@@ -1,9 +1,70 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, {
|
|
3
|
+
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
|
4
|
+
import { Home, ChevronRight } from 'lucide-react';
|
|
4
5
|
import { cn } from '../lib/utils';
|
|
5
6
|
import { ThemeImage, type ImageSource } from '../ui/theme-image';
|
|
6
7
|
|
|
8
|
+
// --- Share Icons (inline SVGs for brand icons) ---
|
|
9
|
+
|
|
10
|
+
const LinkedInIcon = () => (
|
|
11
|
+
<svg width="100%" viewBox="0 0 24 24" fill="none">
|
|
12
|
+
<path
|
|
13
|
+
d="M22.2234 0H1.77187C0.792187 0 0 0.773438 0 1.72969V22.2656C0 23.2219 0.792187 24 1.77187 24H22.2234C23.2031 24 24 23.2219 24 22.2703V1.72969C24 0.773438 23.2031 0 22.2234 0ZM7.12031 20.4516H3.55781V8.99531H7.12031V20.4516ZM5.33906 7.43438C4.19531 7.43438 3.27188 6.51094 3.27188 5.37187C3.27188 4.23281 4.19531 3.30937 5.33906 3.30937C6.47813 3.30937 7.40156 4.23281 7.40156 5.37187C7.40156 6.50625 6.47813 7.43438 5.33906 7.43438ZM20.4516 20.4516H16.8937V14.8828C16.8937 13.5562 16.8703 11.8453 15.0422 11.8453C13.1906 11.8453 12.9094 13.2937 12.9094 14.7891V20.4516H9.35625V8.99531H12.7687V10.5609H12.8156C13.2891 9.66094 14.4516 8.70938 16.1813 8.70938C19.7859 8.70938 20.4516 11.0813 20.4516 14.1656V20.4516Z"
|
|
14
|
+
fill="currentColor"
|
|
15
|
+
/>
|
|
16
|
+
</svg>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const FacebookIcon = () => (
|
|
20
|
+
<svg width="100%" viewBox="0 0 24 24" fill="none">
|
|
21
|
+
<path
|
|
22
|
+
d="M24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 17.9895 4.3882 22.954 10.125 23.8542V15.4688H7.07812V12H10.125V9.35625C10.125 6.34875 11.9166 4.6875 14.6576 4.6875C15.9701 4.6875 17.3438 4.92188 17.3438 4.92188V7.875H15.8306C14.34 7.875 13.875 8.80008 13.875 9.75V12H17.2031L16.6711 15.4688H13.875V23.8542C19.6118 22.954 24 17.9895 24 12Z"
|
|
23
|
+
fill="currentColor"
|
|
24
|
+
/>
|
|
25
|
+
</svg>
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const XIcon = () => (
|
|
29
|
+
<svg width="100%" viewBox="0 0 20 20" fill="none">
|
|
30
|
+
<path
|
|
31
|
+
fillRule="evenodd"
|
|
32
|
+
clipRule="evenodd"
|
|
33
|
+
d="M13.1385 18.75L8.72419 12.458L3.19803 18.75H0.860107L7.68695 10.9793L0.860107 1.25H6.86257L11.023 7.18013L16.2358 1.25H18.5738L12.0637 8.66084L19.141 18.75H13.1385ZM15.742 16.9762H14.1681L4.20766 3.02386H5.78185L9.77107 8.61047L10.4609 9.57989L15.742 16.9762Z"
|
|
34
|
+
fill="currentColor"
|
|
35
|
+
/>
|
|
36
|
+
</svg>
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const RedditIcon = () => (
|
|
40
|
+
<svg width="100%" viewBox="0 0 20 20" fill="none">
|
|
41
|
+
<path d="M7.70781 9.99998C7.13471 9.99998 6.66687 10.4678 6.66687 11.0409C6.66687 11.614 7.13471 12.0819 7.70781 12.0819C8.28091 12.0819 8.74875 11.614 8.74875 11.0409C8.74875 10.4678 8.28091 9.99998 7.70781 9.99998Z" fill="currentColor" />
|
|
42
|
+
<path d="M10.0119 14.5497C10.4096 14.5497 11.7663 14.5029 12.4797 13.7895C12.585 13.6842 12.585 13.5204 12.5031 13.4035C12.3979 13.2982 12.2224 13.2982 12.1172 13.4035C11.661 13.8479 10.7137 14.0117 10.0236 14.0117C9.33354 14.0117 8.37448 13.8479 7.93003 13.4035C7.82477 13.2982 7.64933 13.2982 7.54407 13.4035C7.4388 13.5088 7.4388 13.6842 7.54407 13.7895C8.24582 14.4912 9.61424 14.5497 10.0119 14.5497Z" fill="currentColor" />
|
|
43
|
+
<path d="M11.2517 11.0409C11.2517 11.614 11.7195 12.0819 12.2926 12.0819C12.8657 12.0819 13.3335 11.614 13.3335 11.0409C13.3335 10.4678 12.8657 9.99998 12.2926 9.99998C11.7195 9.99998 11.2517 10.4678 11.2517 11.0409Z" fill="currentColor" />
|
|
44
|
+
<path fillRule="evenodd" clipRule="evenodd" d="M20 10C20 15.5228 15.5228 20 10 20C4.47715 20 0 15.5228 0 10C0 4.47715 4.47715 0 10 0C15.5228 0 20 4.47715 20 10ZM15.2049 8.53799C16.0119 8.53799 16.6669 9.19296 16.6669 9.99998C16.6669 10.5965 16.3043 11.1111 15.8248 11.345C15.8482 11.4854 15.8599 11.6257 15.8599 11.7778C15.8599 14.0234 13.2517 15.8362 10.0236 15.8362C6.79553 15.8362 4.18734 14.0234 4.18734 11.7778C4.18734 11.6257 4.19904 11.4737 4.22243 11.3333C3.70781 11.0994 3.35693 10.5965 3.35693 9.99998C3.35693 9.19296 4.0119 8.53799 4.81892 8.53799C5.20489 8.53799 5.56746 8.70173 5.82477 8.94735C6.83062 8.21051 8.22243 7.75437 9.77799 7.70758L10.5148 4.2222C10.5382 4.15203 10.5733 4.09355 10.6318 4.05846C10.6903 4.02337 10.7604 4.01168 10.8306 4.02337L13.2517 4.53799C13.4154 4.18711 13.7663 3.9532 14.1756 3.9532C14.7487 3.9532 15.2166 4.42103 15.2166 4.99413C15.2166 5.56723 14.7487 6.03507 14.1756 6.03507C13.6142 6.03507 13.1581 5.59062 13.1347 5.04092L10.971 4.58478L10.3043 7.70758C11.8248 7.76606 13.2049 8.2339 14.199 8.94735C14.4563 8.69004 14.8072 8.53799 15.2049 8.53799Z" fill="currentColor" />
|
|
45
|
+
</svg>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const CopyIcon = () => (
|
|
49
|
+
<svg width="100%" viewBox="0 0 20 20" fill="none">
|
|
50
|
+
<path
|
|
51
|
+
d="M8.33326 10.8333C8.69113 11.3118 9.14772 11.7077 9.67205 11.9941C10.1964 12.2806 10.7762 12.4509 11.3721 12.4936C11.9681 12.5363 12.5662 12.4503 13.126 12.2415C13.6858 12.0327 14.1942 11.7059 14.6166 11.2833L17.1166 8.78334C17.8756 7.9975 18.2956 6.94499 18.2861 5.85251C18.2766 4.76002 17.8384 3.71497 17.0658 2.94243C16.2933 2.1699 15.2482 1.7317 14.1558 1.7222C13.0633 1.71271 12.0108 2.13269 11.2249 2.89168L9.79159 4.31668M11.6666 9.16668C11.3087 8.68824 10.8521 8.29236 10.3278 8.00589C9.80347 7.71943 9.22367 7.54908 8.62771 7.5064C8.03176 7.46372 7.4336 7.54971 6.8738 7.75853C6.314 7.96735 5.80566 8.29412 5.38326 8.71668L2.88326 11.2167C2.12426 12.0025 1.70429 13.055 1.71378 14.1475C1.72327 15.24 2.16148 16.2851 2.93401 17.0576C3.70655 17.8301 4.7516 18.2683 5.84408 18.2778C6.93657 18.2873 7.98908 17.8673 8.77492 17.1083L10.1999 15.6833"
|
|
52
|
+
stroke="currentColor"
|
|
53
|
+
strokeWidth="1.66667"
|
|
54
|
+
strokeLinecap="round"
|
|
55
|
+
strokeLinejoin="round"
|
|
56
|
+
/>
|
|
57
|
+
</svg>
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const CheckIcon = () => (
|
|
61
|
+
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
|
|
62
|
+
<path d="M15 4.5L6.75 12.75L3 9" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
63
|
+
</svg>
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// --- Types ---
|
|
67
|
+
|
|
7
68
|
interface TocItem {
|
|
8
69
|
id: string;
|
|
9
70
|
title: string;
|
|
@@ -14,27 +75,94 @@ interface BlogArticleAuthor {
|
|
|
14
75
|
name: string;
|
|
15
76
|
role: string;
|
|
16
77
|
image: ImageSource;
|
|
78
|
+
bio?: string;
|
|
17
79
|
}
|
|
18
80
|
|
|
19
81
|
interface BlogArticleClientProps {
|
|
20
82
|
id?: string;
|
|
21
83
|
children: React.ReactNode;
|
|
84
|
+
title?: string;
|
|
85
|
+
heroImage?: ImageSource;
|
|
86
|
+
blogHref?: string;
|
|
22
87
|
author?: BlogArticleAuthor;
|
|
23
88
|
date?: string;
|
|
24
89
|
readingTime?: number;
|
|
90
|
+
blogLabel?: string;
|
|
91
|
+
tocLabel?: string;
|
|
92
|
+
shareLabel?: string;
|
|
93
|
+
copyLinkLabel?: string;
|
|
94
|
+
copiedLabel?: string;
|
|
95
|
+
aboutAuthorLabel?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// --- Share Buttons ---
|
|
99
|
+
|
|
100
|
+
const shareBtnClass =
|
|
101
|
+
'w-[2.625rem] h-[2.625rem] flex items-center justify-center border rounded-lg border-border text-muted-foreground hover:bg-muted/50 hover:text-foreground transition-colors p-2.5 cursor-pointer';
|
|
102
|
+
|
|
103
|
+
function ShareButtons({
|
|
104
|
+
onCopyLink,
|
|
105
|
+
onShareLinkedIn,
|
|
106
|
+
onShareFacebook,
|
|
107
|
+
onShareX,
|
|
108
|
+
onShareReddit,
|
|
109
|
+
copyLabel,
|
|
110
|
+
copied,
|
|
111
|
+
copiedLabel,
|
|
112
|
+
}: {
|
|
113
|
+
onCopyLink: () => void;
|
|
114
|
+
onShareLinkedIn: () => void;
|
|
115
|
+
onShareFacebook: () => void;
|
|
116
|
+
onShareX: () => void;
|
|
117
|
+
onShareReddit: () => void;
|
|
118
|
+
copyLabel: string;
|
|
119
|
+
copied: boolean;
|
|
120
|
+
copiedLabel: string;
|
|
121
|
+
}) {
|
|
122
|
+
return (
|
|
123
|
+
<div className="flex flex-wrap gap-3">
|
|
124
|
+
<button onClick={onCopyLink} className={shareBtnClass} aria-label={copyLabel} title={copied ? copiedLabel : copyLabel}>
|
|
125
|
+
{copied ? <CheckIcon /> : <CopyIcon />}
|
|
126
|
+
</button>
|
|
127
|
+
<button onClick={onShareLinkedIn} className={shareBtnClass} aria-label="Share on LinkedIn">
|
|
128
|
+
<LinkedInIcon />
|
|
129
|
+
</button>
|
|
130
|
+
<button onClick={onShareFacebook} className={shareBtnClass} aria-label="Share on Facebook">
|
|
131
|
+
<FacebookIcon />
|
|
132
|
+
</button>
|
|
133
|
+
<button onClick={onShareX} className={shareBtnClass} aria-label="Share on X">
|
|
134
|
+
<XIcon />
|
|
135
|
+
</button>
|
|
136
|
+
<button onClick={onShareReddit} className={shareBtnClass} aria-label="Share on Reddit">
|
|
137
|
+
<RedditIcon />
|
|
138
|
+
</button>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
25
141
|
}
|
|
26
142
|
|
|
143
|
+
// --- Main Component ---
|
|
144
|
+
|
|
27
145
|
export function BlogArticleClient({
|
|
28
146
|
id,
|
|
29
147
|
children,
|
|
148
|
+
title,
|
|
149
|
+
heroImage,
|
|
150
|
+
blogHref = '/blog',
|
|
30
151
|
author,
|
|
31
152
|
date,
|
|
32
153
|
readingTime,
|
|
154
|
+
blogLabel = 'Blog',
|
|
155
|
+
tocLabel = 'In this article',
|
|
156
|
+
shareLabel = 'Share article',
|
|
157
|
+
copyLinkLabel = 'Copy link',
|
|
158
|
+
copiedLabel = 'Copied',
|
|
159
|
+
aboutAuthorLabel = 'About the Author',
|
|
33
160
|
}: BlogArticleClientProps) {
|
|
34
161
|
const contentRef = useRef<HTMLDivElement>(null);
|
|
35
162
|
const [tocItems, setTocItems] = useState<TocItem[]>([]);
|
|
36
163
|
const [activeId, setActiveId] = useState('');
|
|
37
164
|
const [activeH2Id, setActiveH2Id] = useState('');
|
|
165
|
+
const [copied, setCopied] = useState(false);
|
|
38
166
|
|
|
39
167
|
// Extract headings from rendered content
|
|
40
168
|
useEffect(() => {
|
|
@@ -44,20 +172,16 @@ export function BlogArticleClient({
|
|
|
44
172
|
const headings = el.querySelectorAll('h2, h3');
|
|
45
173
|
const items: TocItem[] = [];
|
|
46
174
|
headings.forEach((heading) => {
|
|
47
|
-
const
|
|
48
|
-
if (!
|
|
175
|
+
const text = heading.textContent?.trim() || '';
|
|
176
|
+
if (!text || heading.hasAttribute('data-toc-exclude')) return;
|
|
49
177
|
if (!heading.id) {
|
|
50
178
|
heading.id =
|
|
51
|
-
|
|
179
|
+
text
|
|
52
180
|
.toLowerCase()
|
|
53
181
|
.replace(/[^a-z0-9]+/g, '-')
|
|
54
182
|
.replace(/(^-|-$)/g, '') || '';
|
|
55
183
|
}
|
|
56
|
-
items.push({
|
|
57
|
-
id: heading.id,
|
|
58
|
-
title,
|
|
59
|
-
level: heading.tagName === 'H2' ? 2 : 3,
|
|
60
|
-
});
|
|
184
|
+
items.push({ id: heading.id, title: text, level: heading.tagName === 'H2' ? 2 : 3 });
|
|
61
185
|
});
|
|
62
186
|
setTocItems(items);
|
|
63
187
|
});
|
|
@@ -72,11 +196,11 @@ export function BlogArticleClient({
|
|
|
72
196
|
(entries) => {
|
|
73
197
|
for (const entry of entries) {
|
|
74
198
|
if (entry.isIntersecting) {
|
|
75
|
-
const
|
|
76
|
-
setActiveId(
|
|
77
|
-
const tocItem = tocItems.find((t) => t.id ===
|
|
199
|
+
const entryId = entry.target.id;
|
|
200
|
+
setActiveId(entryId);
|
|
201
|
+
const tocItem = tocItems.find((t) => t.id === entryId);
|
|
78
202
|
if (tocItem?.level === 2) {
|
|
79
|
-
setActiveH2Id(
|
|
203
|
+
setActiveH2Id(entryId);
|
|
80
204
|
} else if (tocItem?.level === 3) {
|
|
81
205
|
const idx = tocItems.indexOf(tocItem);
|
|
82
206
|
for (let i = idx - 1; i >= 0; i--) {
|
|
@@ -98,39 +222,142 @@ export function BlogArticleClient({
|
|
|
98
222
|
return () => observer.disconnect();
|
|
99
223
|
}, [tocItems]);
|
|
100
224
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const el = document.getElementById(id);
|
|
225
|
+
const scrollToHeading = useCallback((headingId: string) => {
|
|
226
|
+
const el = document.getElementById(headingId);
|
|
104
227
|
if (el) {
|
|
105
228
|
const top = el.getBoundingClientRect().top + window.scrollY - 100;
|
|
106
229
|
window.scrollTo({ top, behavior: 'smooth' });
|
|
107
230
|
}
|
|
108
231
|
}, []);
|
|
109
232
|
|
|
233
|
+
// Social sharing
|
|
234
|
+
const shareUrl = typeof window !== 'undefined' ? window.location.href : '';
|
|
235
|
+
const shareTitle = title || '';
|
|
236
|
+
const openShare = (url: string) => window.open(url, '_blank', 'noopener,noreferrer,width=600,height=600');
|
|
237
|
+
const shareLinkedIn = () => openShare(`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(shareUrl)}`);
|
|
238
|
+
const shareFacebook = () => openShare(`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareUrl)}`);
|
|
239
|
+
const shareX = () => openShare(`https://twitter.com/intent/tweet?url=${encodeURIComponent(shareUrl)}&text=${encodeURIComponent(shareTitle)}`);
|
|
240
|
+
const shareReddit = () => openShare(`https://reddit.com/submit?url=${encodeURIComponent(shareUrl)}&title=${encodeURIComponent(shareTitle)}`);
|
|
241
|
+
const copyLink = async () => {
|
|
242
|
+
try {
|
|
243
|
+
await navigator.clipboard.writeText(shareUrl);
|
|
244
|
+
setCopied(true);
|
|
245
|
+
setTimeout(() => setCopied(false), 2000);
|
|
246
|
+
} catch { /* noop */ }
|
|
247
|
+
};
|
|
248
|
+
|
|
110
249
|
const formattedDate = date
|
|
111
|
-
? new Date(date + 'T00:00:00').toLocaleDateString(undefined, {
|
|
112
|
-
year: 'numeric',
|
|
113
|
-
month: 'long',
|
|
114
|
-
day: 'numeric',
|
|
115
|
-
})
|
|
250
|
+
? new Date(date + 'T00:00:00').toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })
|
|
116
251
|
: '';
|
|
117
252
|
|
|
253
|
+
const shareProps = {
|
|
254
|
+
onCopyLink: copyLink,
|
|
255
|
+
onShareLinkedIn: shareLinkedIn,
|
|
256
|
+
onShareFacebook: shareFacebook,
|
|
257
|
+
onShareX: shareX,
|
|
258
|
+
onShareReddit: shareReddit,
|
|
259
|
+
copyLabel: copyLinkLabel,
|
|
260
|
+
copied,
|
|
261
|
+
copiedLabel,
|
|
262
|
+
};
|
|
263
|
+
|
|
118
264
|
return (
|
|
119
|
-
<article id={id} className="
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
265
|
+
<article id={id} className="pt-16">
|
|
266
|
+
{/* Breadcrumb + Title + Meta */}
|
|
267
|
+
<div className="mx-auto w-full max-w-[76rem] px-[clamp(1rem,3vw,3rem)] flex flex-col gap-8">
|
|
268
|
+
{/* Breadcrumb */}
|
|
269
|
+
<nav aria-label="Breadcrumb">
|
|
270
|
+
<ul className="flex flex-wrap items-center gap-2">
|
|
271
|
+
<li>
|
|
272
|
+
<a href="/" className="text-sm text-muted-foreground hover:text-foreground flex items-center" aria-label="Home">
|
|
273
|
+
<Home className="w-5 h-5" />
|
|
274
|
+
</a>
|
|
275
|
+
</li>
|
|
276
|
+
<li className="flex items-center">
|
|
277
|
+
<ChevronRight className="w-4 h-4 text-muted-foreground/60" />
|
|
278
|
+
</li>
|
|
279
|
+
<li>
|
|
280
|
+
<a href={blogHref} className="text-sm font-semibold text-muted-foreground hover:text-foreground">
|
|
281
|
+
{blogLabel}
|
|
282
|
+
</a>
|
|
283
|
+
</li>
|
|
284
|
+
{title && (
|
|
285
|
+
<>
|
|
286
|
+
<li className="flex items-center">
|
|
287
|
+
<ChevronRight className="w-4 h-4 text-foreground" />
|
|
288
|
+
</li>
|
|
289
|
+
<li>
|
|
290
|
+
<span className="text-sm font-semibold text-muted-foreground">{title}</span>
|
|
291
|
+
</li>
|
|
292
|
+
</>
|
|
293
|
+
)}
|
|
294
|
+
</ul>
|
|
295
|
+
</nav>
|
|
296
|
+
|
|
297
|
+
{/* Title */}
|
|
298
|
+
{title && (
|
|
299
|
+
<h1 className="text-[clamp(2rem,1.7rem+1.5vw,3rem)] leading-[1.3] font-semibold tracking-[-0.02em] text-foreground">
|
|
300
|
+
{title}
|
|
301
|
+
</h1>
|
|
302
|
+
)}
|
|
303
|
+
|
|
304
|
+
{/* Date + Reading Time */}
|
|
305
|
+
{(formattedDate || readingTime) && (
|
|
306
|
+
<div className="flex items-center gap-3">
|
|
307
|
+
{formattedDate && <span className="text-base text-foreground">{formattedDate}</span>}
|
|
308
|
+
{formattedDate && readingTime && readingTime > 0 && <span className="w-px h-3.5 bg-border" />}
|
|
309
|
+
{readingTime && readingTime > 0 && <span className="text-base text-foreground">{readingTime} min read</span>}
|
|
310
|
+
</div>
|
|
311
|
+
)}
|
|
312
|
+
</div>
|
|
313
|
+
|
|
314
|
+
{/* Two-column layout */}
|
|
315
|
+
<div className="mx-auto w-full max-w-[76rem] px-[clamp(1rem,3vw,3rem)] border-b border-border">
|
|
316
|
+
<div className="h-[clamp(2.5rem,2rem+2.5vw,4rem)]" />
|
|
317
|
+
<div className="flex flex-col lg:flex-row gap-[clamp(2rem,1.7rem+1.5vw,3rem)]">
|
|
318
|
+
{/* Content */}
|
|
319
|
+
<div className="min-w-0 flex-1 max-w-[53.25rem]" ref={contentRef}>
|
|
320
|
+
{heroImage && (
|
|
321
|
+
<ThemeImage
|
|
322
|
+
src={heroImage}
|
|
323
|
+
alt={title || 'Blog article'}
|
|
324
|
+
className="w-full h-auto object-cover mb-10 rounded-2xl"
|
|
325
|
+
/>
|
|
326
|
+
)}
|
|
327
|
+
<div className="blog-content">
|
|
328
|
+
{children}
|
|
329
|
+
</div>
|
|
330
|
+
|
|
331
|
+
{/* About the Author — below content */}
|
|
332
|
+
{author && (
|
|
333
|
+
<section className="bg-muted p-8 rounded-2xl flex flex-col gap-5 mt-12">
|
|
334
|
+
<p className="text-lg font-semibold text-foreground">{aboutAuthorLabel}</p>
|
|
335
|
+
<div className="flex items-center gap-4">
|
|
336
|
+
{author.image && (
|
|
337
|
+
<ThemeImage
|
|
338
|
+
src={author.image}
|
|
339
|
+
alt={author.name}
|
|
340
|
+
className="w-[clamp(2.75rem,2.1rem+3vw,4.5rem)] h-[clamp(2.75rem,2.1rem+3vw,4.5rem)] rounded-full object-cover"
|
|
341
|
+
/>
|
|
342
|
+
)}
|
|
343
|
+
<div>
|
|
344
|
+
<p className="font-medium text-foreground">{author.name}</p>
|
|
345
|
+
{author.role && <p className="text-sm text-muted-foreground">{author.role}</p>}
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
{author.bio && <p className="text-base text-muted-foreground leading-normal">{author.bio}</p>}
|
|
349
|
+
</section>
|
|
350
|
+
)}
|
|
125
351
|
</div>
|
|
126
352
|
|
|
127
353
|
{/* Sidebar */}
|
|
128
|
-
<aside className="hidden lg:block w-
|
|
129
|
-
<div className="sticky top-24 flex flex-col gap-
|
|
354
|
+
<aside className="hidden lg:block w-full max-w-[16rem] xl:max-w-[19.75rem] shrink-0">
|
|
355
|
+
<div className="sticky top-24 flex flex-col gap-5">
|
|
130
356
|
{/* TOC */}
|
|
131
357
|
{tocItems.length > 0 && (
|
|
132
|
-
<
|
|
133
|
-
<
|
|
358
|
+
<div>
|
|
359
|
+
<p className="font-medium text-foreground mb-4">{tocLabel}</p>
|
|
360
|
+
<ul className="flex flex-col gap-2">
|
|
134
361
|
{tocItems.map((item) => {
|
|
135
362
|
const isActive = activeId === item.id;
|
|
136
363
|
if (item.level === 3) {
|
|
@@ -149,66 +376,58 @@ export function BlogArticleClient({
|
|
|
149
376
|
<button
|
|
150
377
|
onClick={() => scrollToHeading(item.id)}
|
|
151
378
|
className={cn(
|
|
152
|
-
'
|
|
153
|
-
isActive
|
|
154
|
-
? 'text-foreground font-medium'
|
|
155
|
-
: 'text-muted-foreground hover:text-foreground',
|
|
379
|
+
'flex items-center gap-2 w-full cursor-pointer text-left',
|
|
380
|
+
isActive ? 'text-foreground font-medium' : 'text-muted-foreground hover:text-foreground',
|
|
156
381
|
)}
|
|
157
382
|
>
|
|
158
|
-
<span
|
|
159
|
-
|
|
160
|
-
'w-3 h-px shrink-0 transition-colors',
|
|
161
|
-
isActive ? 'bg-primary' : 'bg-border',
|
|
162
|
-
)}
|
|
163
|
-
/>
|
|
164
|
-
<span className="text-sm leading-snug line-clamp-1">
|
|
165
|
-
{item.title}
|
|
166
|
-
</span>
|
|
383
|
+
<span className={cn('w-3 h-px shrink-0 transition-colors', isActive ? 'bg-foreground' : 'bg-muted-foreground/40')} />
|
|
384
|
+
<span className="text-sm leading-snug line-clamp-1">{item.title}</span>
|
|
167
385
|
</button>
|
|
168
386
|
</li>
|
|
169
387
|
);
|
|
170
388
|
})}
|
|
171
389
|
</ul>
|
|
172
|
-
</
|
|
390
|
+
</div>
|
|
173
391
|
)}
|
|
174
392
|
|
|
393
|
+
{/* Divider */}
|
|
394
|
+
{tocItems.length > 0 && <hr className="border-border" />}
|
|
395
|
+
|
|
175
396
|
{/* Author */}
|
|
176
397
|
{author && (
|
|
177
|
-
|
|
178
|
-
{
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
className="w-12 h-12 rounded-full object-cover"
|
|
185
|
-
/>
|
|
186
|
-
)}
|
|
187
|
-
<div>
|
|
188
|
-
<p className="text-sm font-medium text-foreground">{author.name}</p>
|
|
189
|
-
{author.role && (
|
|
190
|
-
<p className="text-xs text-muted-foreground">{author.role}</p>
|
|
191
|
-
)}
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
194
|
-
</>
|
|
195
|
-
)}
|
|
196
|
-
|
|
197
|
-
{/* Meta */}
|
|
198
|
-
{(formattedDate || readingTime) && (
|
|
199
|
-
<>
|
|
200
|
-
{(tocItems.length > 0 || author) && !author && (
|
|
201
|
-
<hr className="border-border" />
|
|
398
|
+
<div>
|
|
399
|
+
{author.image && (
|
|
400
|
+
<ThemeImage
|
|
401
|
+
src={author.image}
|
|
402
|
+
alt={author.name}
|
|
403
|
+
className="w-14 h-14 rounded-full object-cover mb-4"
|
|
404
|
+
/>
|
|
202
405
|
)}
|
|
203
|
-
<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
</div>
|
|
207
|
-
</>
|
|
406
|
+
<p className="text-base font-medium text-foreground">{author.name}</p>
|
|
407
|
+
{author.role && <p className="text-sm text-muted-foreground">{author.role}</p>}
|
|
408
|
+
</div>
|
|
208
409
|
)}
|
|
410
|
+
|
|
411
|
+
{/* Divider */}
|
|
412
|
+
{author && <hr className="border-border" />}
|
|
413
|
+
|
|
414
|
+
{/* Share */}
|
|
415
|
+
<div className="flex flex-col gap-4">
|
|
416
|
+
<p className="text-base font-medium text-foreground">{shareLabel}</p>
|
|
417
|
+
<ShareButtons {...shareProps} />
|
|
418
|
+
</div>
|
|
209
419
|
</div>
|
|
210
420
|
</aside>
|
|
211
421
|
</div>
|
|
422
|
+
<div className="h-[clamp(2.5rem,2rem+2.5vw,4rem)]" />
|
|
423
|
+
</div>
|
|
424
|
+
|
|
425
|
+
{/* Mobile share bar */}
|
|
426
|
+
<div className="lg:hidden mx-auto w-full max-w-[76rem] px-[clamp(1rem,3vw,3rem)] mt-8 mb-8">
|
|
427
|
+
<div className="flex flex-col gap-4">
|
|
428
|
+
<p className="text-base font-medium text-foreground">{shareLabel}</p>
|
|
429
|
+
<ShareButtons {...shareProps} />
|
|
430
|
+
</div>
|
|
212
431
|
</div>
|
|
213
432
|
</article>
|
|
214
433
|
);
|