@zoyth/simple-site-framework 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33597,8 +33597,8 @@ function TextToggle({
33597
33597
  onClick: handleClick,
33598
33598
  className: cn(
33599
33599
  "text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors",
33600
- "py-2",
33601
- // Match hamburger button height for mobile alignment
33600
+ "inline-flex items-center h-10",
33601
+ // Match hamburger button height (p-2 + 24px icon = 40px)
33602
33602
  className
33603
33603
  ),
33604
33604
  "aria-label": `Switch to ${displayText}`,
@@ -34210,6 +34210,10 @@ import { useRef as useRef5, useEffect as useEffect10, useState as useState18 } f
34210
34210
  import { compileMDX } from "next-mdx-remote/rsc";
34211
34211
  import rehypeSlug from "rehype-slug";
34212
34212
 
34213
+ // src/lib/content/blog.ts
34214
+ import { compileMDX as compileMDX2 } from "next-mdx-remote/rsc";
34215
+ import rehypeSlug2 from "rehype-slug";
34216
+
34213
34217
  // src/components/sections/HeroSection.tsx
34214
34218
  import Image4 from "next/image";
34215
34219
  import { Fragment as Fragment10, jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
@@ -38109,6 +38113,206 @@ function CTASection({
38109
38113
  }
38110
38114
  );
38111
38115
  }
38116
+
38117
+ // src/components/BlogLayout.tsx
38118
+ import { jsx as jsx69, jsxs as jsxs58 } from "react/jsx-runtime";
38119
+ function BlogLayout({
38120
+ title,
38121
+ excerpt,
38122
+ author,
38123
+ authorAvatar,
38124
+ date,
38125
+ readTime,
38126
+ tags,
38127
+ image,
38128
+ imageAlt,
38129
+ locale,
38130
+ children,
38131
+ showToc = true,
38132
+ backHref,
38133
+ backLabel,
38134
+ className
38135
+ }) {
38136
+ const formattedDate = formatDate2(date, locale);
38137
+ const isFr = locale === "fr";
38138
+ return /* @__PURE__ */ jsxs58("article", { className: cn("max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 sm:py-12", className), children: [
38139
+ /* @__PURE__ */ jsxs58(
38140
+ "a",
38141
+ {
38142
+ href: backHref || `/${locale}/blog`,
38143
+ className: "inline-flex items-center text-sm text-gray-600 hover:text-primary mb-6",
38144
+ children: [
38145
+ /* @__PURE__ */ jsx69("span", { "aria-hidden": "true", children: "\u2190" }),
38146
+ /* @__PURE__ */ jsx69("span", { className: "ml-2", children: backLabel || (isFr ? "Retour au blog" : "Back to blog") })
38147
+ ]
38148
+ }
38149
+ ),
38150
+ /* @__PURE__ */ jsxs58("header", { className: "mb-8 sm:mb-12", children: [
38151
+ /* @__PURE__ */ jsx69("h1", { className: "text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 mb-4", children: title }),
38152
+ /* @__PURE__ */ jsxs58("div", { className: "flex flex-wrap items-center gap-4 text-sm text-gray-600 mb-4", children: [
38153
+ /* @__PURE__ */ jsxs58("div", { className: "flex items-center gap-2", children: [
38154
+ authorAvatar && /* @__PURE__ */ jsx69(
38155
+ "img",
38156
+ {
38157
+ src: authorAvatar,
38158
+ alt: author,
38159
+ className: "w-8 h-8 rounded-full object-cover"
38160
+ }
38161
+ ),
38162
+ /* @__PURE__ */ jsx69("span", { children: author })
38163
+ ] }),
38164
+ /* @__PURE__ */ jsx69("span", { "aria-hidden": "true", children: "\xB7" }),
38165
+ /* @__PURE__ */ jsx69("time", { dateTime: date, children: formattedDate }),
38166
+ /* @__PURE__ */ jsx69("span", { "aria-hidden": "true", children: "\xB7" }),
38167
+ /* @__PURE__ */ jsxs58("span", { children: [
38168
+ readTime,
38169
+ " ",
38170
+ isFr ? "min de lecture" : "min read"
38171
+ ] })
38172
+ ] }),
38173
+ /* @__PURE__ */ jsx69("div", { className: "flex flex-wrap gap-2", children: tags.map((tag) => /* @__PURE__ */ jsx69(
38174
+ "span",
38175
+ {
38176
+ className: "inline-block px-3 py-1 text-xs font-medium bg-gray-100 text-gray-700 rounded-full",
38177
+ children: tag
38178
+ },
38179
+ tag
38180
+ )) })
38181
+ ] }),
38182
+ image && /* @__PURE__ */ jsx69("div", { className: "mb-8 sm:mb-12 rounded-lg overflow-hidden", children: /* @__PURE__ */ jsx69(
38183
+ "img",
38184
+ {
38185
+ src: image,
38186
+ alt: imageAlt || title,
38187
+ className: "w-full h-auto object-cover"
38188
+ }
38189
+ ) }),
38190
+ /* @__PURE__ */ jsxs58("div", { className: "flex flex-col lg:flex-row gap-8 lg:gap-12", children: [
38191
+ showToc && /* @__PURE__ */ jsx69("aside", { className: "hidden lg:block w-64 flex-shrink-0", children: /* @__PURE__ */ jsx69("div", { className: "sticky top-24", children: /* @__PURE__ */ jsx69(TableOfContents, { title: isFr ? "Table des mati\xE8res" : "Table of Contents" }) }) }),
38192
+ /* @__PURE__ */ jsx69("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx69(
38193
+ "div",
38194
+ {
38195
+ className: cn(
38196
+ "prose prose-gray prose-lg max-w-none",
38197
+ "prose-headings:scroll-mt-24",
38198
+ "prose-h2:text-2xl prose-h2:font-bold prose-h2:mt-12 prose-h2:mb-4",
38199
+ "prose-h3:text-xl prose-h3:font-semibold prose-h3:mt-8 prose-h3:mb-3",
38200
+ "prose-h4:text-lg prose-h4:font-semibold prose-h4:mt-6 prose-h4:mb-2",
38201
+ "prose-p:text-gray-700 prose-p:leading-relaxed",
38202
+ "prose-a:text-primary prose-a:no-underline hover:prose-a:underline",
38203
+ "prose-strong:text-gray-900 prose-strong:font-semibold",
38204
+ "prose-ul:my-6 prose-li:my-2",
38205
+ "prose-code:text-primary prose-code:bg-gray-100 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded",
38206
+ "prose-pre:bg-gray-900 prose-pre:text-gray-100",
38207
+ "prose-blockquote:border-l-primary prose-blockquote:bg-gray-50 prose-blockquote:py-2",
38208
+ "prose-table:text-sm",
38209
+ "sm:prose-lg",
38210
+ "prose-headings:break-words",
38211
+ "prose-p:break-words"
38212
+ ),
38213
+ children
38214
+ }
38215
+ ) })
38216
+ ] })
38217
+ ] });
38218
+ }
38219
+ function formatDate2(dateString, locale) {
38220
+ try {
38221
+ const isoDateMatch = dateString.match(/^(\d{4})-(\d{2})-(\d{2})$/);
38222
+ const date = isoDateMatch ? /* @__PURE__ */ new Date(`${dateString}T12:00:00`) : new Date(dateString);
38223
+ return date.toLocaleDateString(locale, {
38224
+ year: "numeric",
38225
+ month: "long",
38226
+ day: "numeric"
38227
+ });
38228
+ } catch {
38229
+ return dateString;
38230
+ }
38231
+ }
38232
+
38233
+ // src/components/BlogIndex.tsx
38234
+ import { useState as useState25, useMemo } from "react";
38235
+ import { jsx as jsx70, jsxs as jsxs59 } from "react/jsx-runtime";
38236
+ function BlogIndex({
38237
+ locale,
38238
+ posts,
38239
+ title,
38240
+ description,
38241
+ showTagFilter = true,
38242
+ cardVariant = "default",
38243
+ featuredFirst = true,
38244
+ className
38245
+ }) {
38246
+ const [activeTag, setActiveTag] = useState25(null);
38247
+ const resolvedTitle = title ? typeof title === "string" ? title : getLocalizedString(title, locale) : void 0;
38248
+ const resolvedDescription = description ? typeof description === "string" ? description : getLocalizedString(description, locale) : void 0;
38249
+ const allTags = useMemo(() => {
38250
+ const tagSet = /* @__PURE__ */ new Set();
38251
+ for (const post of posts) {
38252
+ for (const tag of post.metadata.tags) {
38253
+ tagSet.add(tag);
38254
+ }
38255
+ }
38256
+ return Array.from(tagSet).sort();
38257
+ }, [posts]);
38258
+ const filteredPosts = activeTag ? posts.filter((p) => p.metadata.tags.includes(activeTag)) : posts;
38259
+ const featuredPosts = featuredFirst ? filteredPosts.filter((p) => p.metadata.featured) : [];
38260
+ const regularPosts = featuredFirst ? filteredPosts.filter((p) => !p.metadata.featured) : filteredPosts;
38261
+ const isFr = locale === "fr";
38262
+ return /* @__PURE__ */ jsxs59("div", { className: cn("max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 sm:py-12", className), children: [
38263
+ (resolvedTitle || resolvedDescription) && /* @__PURE__ */ jsxs59("div", { className: "mb-8 sm:mb-12", children: [
38264
+ resolvedTitle && /* @__PURE__ */ jsx70("h1", { className: "text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 mb-3", children: resolvedTitle }),
38265
+ resolvedDescription && /* @__PURE__ */ jsx70("p", { className: "text-lg text-gray-600", children: resolvedDescription })
38266
+ ] }),
38267
+ showTagFilter && allTags.length > 0 && /* @__PURE__ */ jsx70("div", { className: "flex flex-wrap gap-2 mb-8", role: "group", "aria-label": isFr ? "Filtrer par tag" : "Filter by tag", children: allTags.map((tag) => /* @__PURE__ */ jsx70(
38268
+ "button",
38269
+ {
38270
+ onClick: () => setActiveTag(activeTag === tag ? null : tag),
38271
+ className: cn(
38272
+ "px-3 py-1.5 text-sm font-medium rounded-full transition-colors",
38273
+ activeTag === tag ? "bg-primary text-white" : "bg-gray-100 text-gray-700 hover:bg-gray-200"
38274
+ ),
38275
+ children: tag
38276
+ },
38277
+ tag
38278
+ )) }),
38279
+ filteredPosts.length === 0 && /* @__PURE__ */ jsx70("div", { className: "text-center py-16", children: /* @__PURE__ */ jsx70("p", { className: "text-gray-500 text-lg", children: isFr ? "Aucun article trouv\xE9." : "No posts found." }) }),
38280
+ featuredPosts.length > 0 && /* @__PURE__ */ jsx70("div", { className: "mb-12", children: /* @__PURE__ */ jsx70("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: featuredPosts.map((post) => /* @__PURE__ */ jsx70(
38281
+ BlogCard,
38282
+ {
38283
+ locale,
38284
+ title: post.metadata.title,
38285
+ excerpt: post.metadata.excerpt,
38286
+ image: post.metadata.image,
38287
+ imageAlt: post.metadata.imageAlt,
38288
+ href: `/${locale}/blog/${post.slug}`,
38289
+ author: post.metadata.author,
38290
+ date: post.metadata.date,
38291
+ readTime: post.metadata.readTime,
38292
+ tags: post.metadata.tags,
38293
+ variant: cardVariant
38294
+ },
38295
+ post.slug
38296
+ )) }) }),
38297
+ regularPosts.length > 0 && /* @__PURE__ */ jsx70("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6", children: regularPosts.map((post) => /* @__PURE__ */ jsx70(
38298
+ BlogCard,
38299
+ {
38300
+ locale,
38301
+ title: post.metadata.title,
38302
+ excerpt: post.metadata.excerpt,
38303
+ image: post.metadata.image,
38304
+ imageAlt: post.metadata.imageAlt,
38305
+ href: `/${locale}/blog/${post.slug}`,
38306
+ author: post.metadata.author,
38307
+ date: post.metadata.date,
38308
+ readTime: post.metadata.readTime,
38309
+ tags: post.metadata.tags,
38310
+ variant: cardVariant
38311
+ },
38312
+ post.slug
38313
+ )) })
38314
+ ] });
38315
+ }
38112
38316
  export {
38113
38317
  AboutSection,
38114
38318
  AddressLink,
@@ -38116,6 +38320,8 @@ export {
38116
38320
  AnimatedItem,
38117
38321
  AnimatedSection,
38118
38322
  BlogCard,
38323
+ BlogIndex,
38324
+ BlogLayout,
38119
38325
  BodyEndScripts,
38120
38326
  Breadcrumb,
38121
38327
  Button,