react-layout-sdk 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # React Layout SDK (JD Layout System)
2
+
3
+ The **React Layout SDK** is an enterprise-grade Frontend SDK designed to consume layout definitions from Strapi via the **Strapi Layout Plugin**. Inspired by robust headless ecosystems like Sitecore JSS, this SDK provides dynamic placeholder routing and a scaffold generator to stream-line Developer Experience (DX).
4
+
5
+ ## Installation
6
+
7
+ Install the package using your favorite package manager:
8
+
9
+ ```bash
10
+ npm install react-layout-sdk
11
+ ```
12
+
13
+ ## Quick Start (Project Scaffolding)
14
+
15
+ To quickly set up your Next.js project to work with the JD Layout System, run the init command at the root of your project:
16
+
17
+ ```bash
18
+ npx react-layout-sdk init
19
+ ```
20
+
21
+ **What this does:**
22
+ 1. Creates a `src/components/factory.tsx` file for component mapping.
23
+ 2. Generates base boilerplate components like `Header.tsx` and `Footer.tsx`.
24
+ 3. Auto-configures your Next.js App Router (`app/[[...slug]]/page.tsx`) or Pages Router to catch all layout routes and fetch them dynamically using `fetchJDLayout`.
25
+
26
+ ## Component Scaffolding
27
+
28
+ You can scaffold new React components and automatically map them to their corresponding Strapi components in `factory.tsx` using the generate command:
29
+
30
+ ```bash
31
+ npx react-layout-sdk generate component <strapi-component-uid>
32
+ ```
33
+
34
+ **Example:**
35
+ ```bash
36
+ npx react-layout-sdk generate component blocks.hero-banner
37
+ ```
38
+ *This command will:*
39
+ - Create `src/components/HeroBanner.tsx`.
40
+ - Automatically inject `import HeroBanner` and map `'blocks.hero-banner': HeroBanner` inside your `factory.tsx`.
41
+
42
+ ## API Reference
43
+
44
+ ### 1. `fetchJDLayout(apiUrl, slug, locale)`
45
+ Fetches the layout structure and content from the Strapi backend.
46
+
47
+ ```typescript
48
+ import { fetchJDLayout } from 'react-layout-sdk';
49
+
50
+ const layoutData = await fetchJDLayout('http://localhost:1337', '/home', 'en');
51
+ const { route } = layoutData.strapi;
52
+ ```
53
+
54
+ ### 2. `<Placeholder />`
55
+ Renders a specific dynamic zone (like `header`, `main`, or `footer`) by matching the JSON data against your React components.
56
+
57
+ ```tsx
58
+ import { Placeholder } from 'react-layout-sdk';
59
+ import { componentMap } from '@/components/factory';
60
+
61
+ <main>
62
+ <Placeholder
63
+ name="main"
64
+ rendering={route.placeholders.main || []}
65
+ componentMap={componentMap}
66
+ />
67
+ </main>
68
+ ```
69
+
70
+ ## TypeScript Support
71
+
72
+ This package comes with full TypeScript definitions, including `JDLayoutResponse`, `JDRoute`, and `JDContext`.
package/bin/init.js CHANGED
@@ -128,7 +128,7 @@ if (command === 'init') {
128
128
 
129
129
  // 2. Create Header.tsx
130
130
  const headerPath = path.join(componentsDir, 'Header.tsx');
131
- const headerContent = `import React from 'react';\n\nexport default function Header(props: any) {\n const title = props?.title || 'Velox Header';\n const logoUrl = props?.logoUrl;\n\n return (\n <header style={{ padding: '20px', background: '#f5f5f5', borderBottom: '1px solid #ddd' }}>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>\n {logoUrl && <img src={logoUrl} alt="Logo" width="40" />}\n <h1 style={{ margin: 0, fontSize: '1.5rem' }}>{title}</h1>\n </div>\n </div>\n </header>\n );\n}\n`;
131
+ const headerContent = `import React from 'react';\n\nexport default function Header(props: any) {\n const title = props?.title || 'JD Header';\n const logoUrl = props?.logoUrl;\n\n return (\n <header style={{ padding: '20px', background: '#f5f5f5', borderBottom: '1px solid #ddd' }}>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>\n {logoUrl && <img src={logoUrl} alt="Logo" width="40" />}\n <h1 style={{ margin: 0, fontSize: '1.5rem' }}>{title}</h1>\n </div>\n </div>\n </header>\n );\n}\n`;
132
132
  if (!fs.existsSync(headerPath)) {
133
133
  fs.writeFileSync(headerPath, headerContent);
134
134
  console.log('✅ Created components/Header.tsx');
@@ -136,7 +136,7 @@ if (command === 'init') {
136
136
 
137
137
  // 3. Create Footer.tsx
138
138
  const footerPath = path.join(componentsDir, 'Footer.tsx');
139
- const footerContent = `import React from 'react';\n\nexport default function Footer(props: any) {\n const text = props?.text || '© 2026 Velox Layout';\n return (\n <footer style={{ padding: '20px', background: '#333', color: '#fff', textAlign: 'center', marginTop: '40px' }}>\n <p style={{ margin: 0 }}>{text}</p>\n </footer>\n );\n}\n`;
139
+ const footerContent = `import React from 'react';\n\nexport default function Footer(props: any) {\n const text = props?.text || '© 2026 JD Layout';\n return (\n <footer style={{ padding: '20px', background: '#333', color: '#fff', textAlign: 'center', marginTop: '40px' }}>\n <p style={{ margin: 0 }}>{text}</p>\n </footer>\n );\n}\n`;
140
140
  if (!fs.existsSync(footerPath)) {
141
141
  fs.writeFileSync(footerPath, footerContent);
142
142
  console.log('✅ Created components/Footer.tsx');
@@ -157,7 +157,7 @@ if (command === 'init') {
157
157
  ensureDirSync(pageDir);
158
158
 
159
159
  const appPagePath = path.join(pageDir, 'page.tsx');
160
- const appPageContent = `import React from 'react';\nimport { fetchVeloxLayout, Placeholder } from 'react-layout-sdk';\nimport { componentMap } from '@/components/factory';\n\nexport default async function Page({ params }: { params: Promise<{ slug?: string[] }> }) {\n const resolvedParams = await params;\n const slugArray = resolvedParams?.slug || [];\n const path = slugArray.join('/') || '/';\n const STRAPI_URL = process.env.NEXT_PUBLIC_STRAPI_URL || 'http://localhost:1337';\n \n try {\n const layoutData = await fetchVeloxLayout(STRAPI_URL, path, 'en');\n if (!layoutData || !layoutData.strapi) return <h1>404 - Not Found</h1>;\n\n const { route } = layoutData.strapi;\n\n return (\n <div className="layout-wrapper">\n <Placeholder name="header" rendering={route.placeholders.header || []} componentMap={componentMap} />\n <main style={{ minHeight: '60vh', padding: '20px' }}>\n <Placeholder name="main" rendering={route.placeholders.main || []} componentMap={componentMap} />\n </main>\n <Placeholder name="footer" rendering={route.placeholders.footer || []} componentMap={componentMap} />\n </div>\n );\n } catch (error) {\n return <h1>Error Loading Layout</h1>;\n }\n}\n`;
160
+ const appPageContent = `import React from 'react';\nimport { fetchJDLayout, Placeholder } from 'react-layout-sdk';\nimport { componentMap } from '@/components/factory';\n\nexport default async function Page({ params }: { params: Promise<{ slug?: string[] }> }) {\n const resolvedParams = await params;\n const slugArray = resolvedParams?.slug || [];\n const path = slugArray.join('/') || '/';\n const STRAPI_URL = process.env.NEXT_PUBLIC_STRAPI_URL || 'http://localhost:1337';\n \n try {\n const layoutData = await fetchJDLayout(STRAPI_URL, path, 'en');\n if (!layoutData || !layoutData.strapi) return <h1>404 - Not Found</h1>;\n\n const { route } = layoutData.strapi;\n\n return (\n <div className="layout-wrapper">\n <Placeholder name="header" rendering={route.placeholders.header || []} componentMap={componentMap} />\n <main style={{ minHeight: '60vh', padding: '20px' }}>\n <Placeholder name="main" rendering={route.placeholders.main || []} componentMap={componentMap} />\n </main>\n <Placeholder name="footer" rendering={route.placeholders.footer || []} componentMap={componentMap} />\n </div>\n );\n } catch (error) {\n return <h1>Error Loading Layout</h1>;\n }\n}\n`;
161
161
  if (!fs.existsSync(appPagePath)) {
162
162
  fs.writeFileSync(appPagePath, appPageContent);
163
163
  console.log('✅ Created app/[[...slug]]/page.tsx');
@@ -173,7 +173,7 @@ if (command === 'init') {
173
173
  }
174
174
 
175
175
  const pagePath = path.join(basePath, 'pages', '[[...slug]].tsx');
176
- const pagesContent = `import React from 'react';\nimport { fetchVeloxLayout, Placeholder } from 'react-layout-sdk';\nimport { componentMap } from '@/components/factory';\n\nexport default function LayoutPage({ layoutData, error }: any) {\n if (error || !layoutData?.strapi) return <h1>404 - Layout Not Found</h1>;\n\n const { route } = layoutData.strapi;\n return (\n <div className="layout-wrapper">\n <Placeholder name="header" rendering={route.placeholders.header || []} componentMap={componentMap} />\n <main style={{ minHeight: '60vh', padding: '20px' }}>\n <Placeholder name="main" rendering={route.placeholders.main || []} componentMap={componentMap} />\n </main>\n <Placeholder name="footer" rendering={route.placeholders.footer || []} componentMap={componentMap} />\n </div>\n );\n}\n\nexport async function getServerSideProps(context: any) {\n const slugArray = context.params?.slug || [];\n const path = slugArray.join('/') || '/';\n const STRAPI_URL = process.env.NEXT_PUBLIC_STRAPI_URL || 'http://localhost:1337';\n\n try {\n const layoutData = await fetchVeloxLayout(STRAPI_URL, path, 'en');\n if (!layoutData) return { notFound: true };\n\n return { props: { layoutData } };\n } catch (error) {\n return { props: { error: true } };\n }\n}\n`;
176
+ const pagesContent = `import React from 'react';\nimport { fetchJDLayout, Placeholder } from 'react-layout-sdk';\nimport { componentMap } from '@/components/factory';\n\nexport default function LayoutPage({ layoutData, error }: any) {\n if (error || !layoutData?.strapi) return <h1>404 - Layout Not Found</h1>;\n\n const { route } = layoutData.strapi;\n return (\n <div className="layout-wrapper">\n <Placeholder name="header" rendering={route.placeholders.header || []} componentMap={componentMap} />\n <main style={{ minHeight: '60vh', padding: '20px' }}>\n <Placeholder name="main" rendering={route.placeholders.main || []} componentMap={componentMap} />\n </main>\n <Placeholder name="footer" rendering={route.placeholders.footer || []} componentMap={componentMap} />\n </div>\n );\n}\n\nexport async function getServerSideProps(context: any) {\n const slugArray = context.params?.slug || [];\n const path = slugArray.join('/') || '/';\n const STRAPI_URL = process.env.NEXT_PUBLIC_STRAPI_URL || 'http://localhost:1337';\n\n try {\n const layoutData = await fetchJDLayout(STRAPI_URL, path, 'en');\n if (!layoutData) return { notFound: true };\n\n return { props: { layoutData } };\n } catch (error) {\n return { props: { error: true } };\n }\n}\n`;
177
177
  if (!fs.existsSync(pagePath)) {
178
178
  fs.writeFileSync(pagePath, pagesContent);
179
179
  console.log('✅ Created pages/[[...slug]].tsx');
package/dist/index.d.mts CHANGED
@@ -16,27 +16,34 @@ interface PlaceholderProps {
16
16
  }
17
17
  declare const Placeholder: React.FC<PlaceholderProps>;
18
18
 
19
- interface VeloxPlaceholderData {
19
+ interface JDPlaceholderData {
20
20
  __component: string;
21
21
  [key: string]: any;
22
22
  }
23
- interface VeloxRoute {
23
+ interface JDRoute {
24
24
  name: string;
25
25
  displayName: string;
26
- placeholders: Record<string, VeloxPlaceholderData[]>;
26
+ fields?: Record<string, any>;
27
+ seo?: {
28
+ metaTitle?: string;
29
+ metaDescription?: string;
30
+ metaImage?: any;
31
+ [key: string]: any;
32
+ };
33
+ placeholders: Record<string, JDPlaceholderData[]>;
27
34
  }
28
- interface VeloxContext {
35
+ interface JDContext {
29
36
  pageEditing: boolean;
30
37
  site: Record<string, any>;
31
38
  language: string;
32
39
  locales: any[];
33
40
  }
34
- interface VeloxLayoutResponse {
41
+ interface JDLayoutResponse {
35
42
  strapi: {
36
- context: VeloxContext;
37
- route: VeloxRoute;
43
+ context: JDContext;
44
+ route: JDRoute;
38
45
  };
39
46
  }
40
- declare const fetchVeloxLayout: (apiUrl: string, slug: string, locale?: string, options?: RequestInit) => Promise<VeloxLayoutResponse | null>;
47
+ declare const fetchJDLayout: (apiUrl: string, slug: string, locale?: string, options?: RequestInit) => Promise<JDLayoutResponse | null>;
41
48
 
42
- export { ComponentFactory, type ComponentFactoryProps, Placeholder, type PlaceholderProps, type VeloxContext, type VeloxLayoutResponse, type VeloxPlaceholderData, type VeloxRoute, fetchVeloxLayout };
49
+ export { ComponentFactory, type ComponentFactoryProps, type JDContext, type JDLayoutResponse, type JDPlaceholderData, type JDRoute, Placeholder, type PlaceholderProps, fetchJDLayout };
package/dist/index.d.ts CHANGED
@@ -16,27 +16,34 @@ interface PlaceholderProps {
16
16
  }
17
17
  declare const Placeholder: React.FC<PlaceholderProps>;
18
18
 
19
- interface VeloxPlaceholderData {
19
+ interface JDPlaceholderData {
20
20
  __component: string;
21
21
  [key: string]: any;
22
22
  }
23
- interface VeloxRoute {
23
+ interface JDRoute {
24
24
  name: string;
25
25
  displayName: string;
26
- placeholders: Record<string, VeloxPlaceholderData[]>;
26
+ fields?: Record<string, any>;
27
+ seo?: {
28
+ metaTitle?: string;
29
+ metaDescription?: string;
30
+ metaImage?: any;
31
+ [key: string]: any;
32
+ };
33
+ placeholders: Record<string, JDPlaceholderData[]>;
27
34
  }
28
- interface VeloxContext {
35
+ interface JDContext {
29
36
  pageEditing: boolean;
30
37
  site: Record<string, any>;
31
38
  language: string;
32
39
  locales: any[];
33
40
  }
34
- interface VeloxLayoutResponse {
41
+ interface JDLayoutResponse {
35
42
  strapi: {
36
- context: VeloxContext;
37
- route: VeloxRoute;
43
+ context: JDContext;
44
+ route: JDRoute;
38
45
  };
39
46
  }
40
- declare const fetchVeloxLayout: (apiUrl: string, slug: string, locale?: string, options?: RequestInit) => Promise<VeloxLayoutResponse | null>;
47
+ declare const fetchJDLayout: (apiUrl: string, slug: string, locale?: string, options?: RequestInit) => Promise<JDLayoutResponse | null>;
41
48
 
42
- export { ComponentFactory, type ComponentFactoryProps, Placeholder, type PlaceholderProps, type VeloxContext, type VeloxLayoutResponse, type VeloxPlaceholderData, type VeloxRoute, fetchVeloxLayout };
49
+ export { ComponentFactory, type ComponentFactoryProps, type JDContext, type JDLayoutResponse, type JDPlaceholderData, type JDRoute, Placeholder, type PlaceholderProps, fetchJDLayout };
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ComponentFactory: () => ComponentFactory,
34
34
  Placeholder: () => Placeholder,
35
- fetchVeloxLayout: () => fetchVeloxLayout
35
+ fetchJDLayout: () => fetchJDLayout
36
36
  });
37
37
  module.exports = __toCommonJS(index_exports);
38
38
 
@@ -47,7 +47,7 @@ var ComponentFactory = ({
47
47
  }) => {
48
48
  const Component = componentMap[componentName];
49
49
  if (!Component) {
50
- console.warn(`[Velox SDK] Component not found for: ${componentName}`);
50
+ console.warn(`[JD SDK] Component not found for: ${componentName}`);
51
51
  return /* @__PURE__ */ import_react.default.createElement("div", { style: {
52
52
  padding: "2rem",
53
53
  margin: "1rem",
@@ -87,19 +87,19 @@ var Placeholder = ({ name, rendering, customProps, componentMap }) => {
87
87
  };
88
88
 
89
89
  // src/index.ts
90
- var fetchVeloxLayout = async (apiUrl, slug, locale = "en", options) => {
90
+ var fetchJDLayout = async (apiUrl, slug, locale = "en", options) => {
91
91
  try {
92
92
  const querySymbol = slug.includes("?") ? "&" : "?";
93
93
  const res = await fetch(`${apiUrl}/api/layout/${slug}${querySymbol}locale=${locale}`, {
94
94
  ...options
95
95
  });
96
96
  if (!res.ok) {
97
- console.error(`[Velox SDK] Failed to fetch layout: ${res.statusText}`);
97
+ console.error(`[JD SDK] Failed to fetch layout: ${res.statusText}`);
98
98
  return null;
99
99
  }
100
100
  return await res.json();
101
101
  } catch (error) {
102
- console.error(`[Velox SDK] Network error fetching layout`, error);
102
+ console.error(`[JD SDK] Network error fetching layout`, error);
103
103
  return null;
104
104
  }
105
105
  };
@@ -107,5 +107,5 @@ var fetchVeloxLayout = async (apiUrl, slug, locale = "en", options) => {
107
107
  0 && (module.exports = {
108
108
  ComponentFactory,
109
109
  Placeholder,
110
- fetchVeloxLayout
110
+ fetchJDLayout
111
111
  });
package/dist/index.mjs CHANGED
@@ -9,7 +9,7 @@ var ComponentFactory = ({
9
9
  }) => {
10
10
  const Component = componentMap[componentName];
11
11
  if (!Component) {
12
- console.warn(`[Velox SDK] Component not found for: ${componentName}`);
12
+ console.warn(`[JD SDK] Component not found for: ${componentName}`);
13
13
  return /* @__PURE__ */ React.createElement("div", { style: {
14
14
  padding: "2rem",
15
15
  margin: "1rem",
@@ -49,24 +49,24 @@ var Placeholder = ({ name, rendering, customProps, componentMap }) => {
49
49
  };
50
50
 
51
51
  // src/index.ts
52
- var fetchVeloxLayout = async (apiUrl, slug, locale = "en", options) => {
52
+ var fetchJDLayout = async (apiUrl, slug, locale = "en", options) => {
53
53
  try {
54
54
  const querySymbol = slug.includes("?") ? "&" : "?";
55
55
  const res = await fetch(`${apiUrl}/api/layout/${slug}${querySymbol}locale=${locale}`, {
56
56
  ...options
57
57
  });
58
58
  if (!res.ok) {
59
- console.error(`[Velox SDK] Failed to fetch layout: ${res.statusText}`);
59
+ console.error(`[JD SDK] Failed to fetch layout: ${res.statusText}`);
60
60
  return null;
61
61
  }
62
62
  return await res.json();
63
63
  } catch (error) {
64
- console.error(`[Velox SDK] Network error fetching layout`, error);
64
+ console.error(`[JD SDK] Network error fetching layout`, error);
65
65
  return null;
66
66
  }
67
67
  };
68
68
  export {
69
69
  ComponentFactory,
70
70
  Placeholder,
71
- fetchVeloxLayout
71
+ fetchJDLayout
72
72
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-layout-sdk",
3
- "version": "1.1.5",
4
- "description": "React components for Velox SDK (Sitecore-like routing)",
3
+ "version": "1.1.7",
4
+ "description": "React components for JD SDK (Sitecore-like routing)",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",
@@ -18,7 +18,7 @@ export const ComponentFactory: React.FC<ComponentFactoryProps> = ({
18
18
  const Component = componentMap[componentName];
19
19
 
20
20
  if (!Component) {
21
- console.warn(`[Velox SDK] Component not found for: ${componentName}`);
21
+ console.warn(`[JD SDK] Component not found for: ${componentName}`);
22
22
  return (
23
23
  <div style={{
24
24
  padding: '2rem',
package/src/index.ts CHANGED
@@ -2,38 +2,45 @@ export { Placeholder, ComponentFactory } from './Placeholder';
2
2
  export type { PlaceholderProps, ComponentFactoryProps } from './Placeholder';
3
3
 
4
4
  // Useful type definitions for the API response
5
- export interface VeloxPlaceholderData {
5
+ export interface JDPlaceholderData {
6
6
  __component: string;
7
7
  [key: string]: any;
8
8
  }
9
9
 
10
- export interface VeloxRoute {
10
+ export interface JDRoute {
11
11
  name: string;
12
12
  displayName: string;
13
- placeholders: Record<string, VeloxPlaceholderData[]>;
13
+ fields?: Record<string, any>;
14
+ seo?: {
15
+ metaTitle?: string;
16
+ metaDescription?: string;
17
+ metaImage?: any;
18
+ [key: string]: any;
19
+ };
20
+ placeholders: Record<string, JDPlaceholderData[]>;
14
21
  }
15
22
 
16
- export interface VeloxContext {
23
+ export interface JDContext {
17
24
  pageEditing: boolean;
18
25
  site: Record<string, any>;
19
26
  language: string;
20
27
  locales: any[];
21
28
  }
22
29
 
23
- export interface VeloxLayoutResponse {
30
+ export interface JDLayoutResponse {
24
31
  strapi: {
25
- context: VeloxContext;
26
- route: VeloxRoute;
32
+ context: JDContext;
33
+ route: JDRoute;
27
34
  }
28
35
  }
29
36
 
30
37
  // Hook / Utility function to fetch layout
31
- export const fetchVeloxLayout = async (
38
+ export const fetchJDLayout = async (
32
39
  apiUrl: string,
33
40
  slug: string,
34
41
  locale: string = 'en',
35
42
  options?: RequestInit
36
- ): Promise<VeloxLayoutResponse | null> => {
43
+ ): Promise<JDLayoutResponse | null> => {
37
44
  try {
38
45
  const querySymbol = slug.includes('?') ? '&' : '?';
39
46
  const res = await fetch(`${apiUrl}/api/layout/${slug}${querySymbol}locale=${locale}`, {
@@ -41,13 +48,13 @@ export const fetchVeloxLayout = async (
41
48
  });
42
49
 
43
50
  if (!res.ok) {
44
- console.error(`[Velox SDK] Failed to fetch layout: ${res.statusText}`);
51
+ console.error(`[JD SDK] Failed to fetch layout: ${res.statusText}`);
45
52
  return null;
46
53
  }
47
54
 
48
- return await res.json() as VeloxLayoutResponse;
55
+ return await res.json() as JDLayoutResponse;
49
56
  } catch (error) {
50
- console.error(`[Velox SDK] Network error fetching layout`, error);
57
+ console.error(`[JD SDK] Network error fetching layout`, error);
51
58
  return null;
52
59
  }
53
60
  }