@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.
@@ -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;CACf;AAED,UAAU,gBAAgB;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,gBAAgB,2CAMxF"}
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"}
@@ -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;AAiBxD,MAAM,UAAU,WAAW,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAoB;IACvF,OAAO,CACL,KAAC,iBAAiB,IAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,YAC5E,QAAQ,GACS,CACrB,CAAC;AACJ,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;AAExE,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQjE,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,CAAC;CACpB;AAED,UAAU,sBAAsB;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,EAAE,EACF,QAAQ,EACR,MAAM,EACN,IAAI,EACJ,WAAW,GACZ,EAAE,sBAAsB,2CAsLxB"}
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 { useCallback, useEffect, useRef, useState } from 'react';
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
- export function BlogArticleClient({ id, children, author, date, readingTime, }) {
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 title = heading.textContent?.trim() || '';
21
- if (!title || heading.hasAttribute('data-toc-exclude'))
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
- title
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 id = entry.target.id;
49
- setActiveId(id);
50
- const tocItem = tocItems.find((t) => t.id === 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(id);
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
- // Smooth scroll to heading
74
- const scrollToHeading = useCallback((id) => {
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
- return (_jsx("article", { id: id, className: "py-12 md:py-20", children: _jsx("div", { className: "mx-auto w-full max-w-[76rem] px-[clamp(1rem,3vw,3rem)]", children: _jsxs("div", { className: "flex gap-10 lg:gap-16", children: [_jsx("div", { ref: contentRef, className: "blog-content min-w-0 flex-1 max-w-3xl", children: children }), _jsx("aside", { className: "hidden lg:block w-64 shrink-0", children: _jsxs("div", { className: "sticky top-24 flex flex-col gap-6", children: [tocItems.length > 0 && (_jsx("nav", { children: _jsx("ul", { className: "flex flex-col", children: tocItems.map((item) => {
89
- const isActive = activeId === item.id;
90
- if (item.level === 3) {
91
- const idx = tocItems.indexOf(item);
92
- let parentH2Id = '';
93
- for (let i = idx - 1; i >= 0; i--) {
94
- if (tocItems[i].level === 2) {
95
- parentH2Id = tocItems[i].id;
96
- break;
97
- }
98
- }
99
- if (parentH2Id !== activeH2Id)
100
- return null;
101
- }
102
- return (_jsx("li", { className: item.level === 3 ? 'pl-3' : '', children: _jsxs("button", { onClick: () => scrollToHeading(item.id), className: cn('my-0.5 flex items-center gap-2 w-full cursor-pointer text-left', isActive
103
- ? 'text-foreground font-medium'
104
- : 'text-muted-foreground hover:text-foreground'), children: [_jsx("span", { className: cn('w-3 h-px shrink-0 transition-colors', isActive ? 'bg-primary' : 'bg-border') }), _jsx("span", { className: "text-sm leading-snug line-clamp-1", children: item.title })] }) }, item.id));
105
- }) }) })), author && (_jsxs(_Fragment, { children: [tocItems.length > 0 && _jsx("hr", { className: "border-border" }), _jsxs("div", { className: "flex flex-col gap-3", children: [author.image && (_jsx(ThemeImage, { src: author.image, alt: author.name, className: "w-12 h-12 rounded-full object-cover" })), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: author.name }), author.role && (_jsx("p", { className: "text-xs text-muted-foreground", children: author.role }))] })] })] })), (formattedDate || readingTime) && (_jsxs(_Fragment, { children: [(tocItems.length > 0 || author) && !author && (_jsx("hr", { className: "border-border" })), _jsxs("div", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [formattedDate && _jsx("span", { children: formattedDate }), readingTime ? _jsxs("span", { children: [readingTime, " min read"] }) : null] })] }))] }) })] }) }) }));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipsite.dev/components",
3
- "version": "0.2.30",
3
+ "version": "0.2.32",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/shipsite/shipsite",
@@ -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({ id, children, author, date, readingTime }: BlogArticleProps) {
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 id={id} author={author} date={date} readingTime={readingTime}>
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, { useCallback, useEffect, useRef, useState } from '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 title = heading.textContent?.trim() || '';
48
- if (!title || heading.hasAttribute('data-toc-exclude')) return;
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
- title
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 id = entry.target.id;
76
- setActiveId(id);
77
- const tocItem = tocItems.find((t) => t.id === 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(id);
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
- // Smooth scroll to heading
102
- const scrollToHeading = useCallback((id: string) => {
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="py-12 md:py-20">
120
- <div className="mx-auto w-full max-w-[76rem] px-[clamp(1rem,3vw,3rem)]">
121
- <div className="flex gap-10 lg:gap-16">
122
- {/* Main content */}
123
- <div ref={contentRef} className="blog-content min-w-0 flex-1 max-w-3xl">
124
- {children}
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-64 shrink-0">
129
- <div className="sticky top-24 flex flex-col gap-6">
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
- <nav>
133
- <ul className="flex flex-col">
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
- 'my-0.5 flex items-center gap-2 w-full cursor-pointer text-left',
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
- className={cn(
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
- </nav>
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
- {tocItems.length > 0 && <hr className="border-border" />}
179
- <div className="flex flex-col gap-3">
180
- {author.image && (
181
- <ThemeImage
182
- src={author.image}
183
- alt={author.name}
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
- <div className="flex flex-col gap-1 text-xs text-muted-foreground">
204
- {formattedDate && <span>{formattedDate}</span>}
205
- {readingTime ? <span>{readingTime} min read</span> : null}
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
  );