Chris Tate 4 ヶ月 前
コミット
bccedc2459

+ 422 - 0
apps/web/app/(main)/docs/api/react-pdf/page.mdx

@@ -0,0 +1,422 @@
+export const metadata = { title: "@json-render/react-pdf API" }
+
+# @json-render/react-pdf
+
+PDF document renderer. Turn JSON specs into PDFs using `@react-pdf/renderer`.
+
+## Install
+
+```bash
+npm install @json-render/core @json-render/react-pdf
+```
+
+See the [React PDF example](https://github.com/vercel-labs/json-render/tree/main/examples/react-pdf) for a full working example.
+
+## schema
+
+The PDF element schema for document specs. Use with `defineCatalog` from core.
+
+```typescript
+import { defineCatalog } from '@json-render/core';
+import { schema, standardComponentDefinitions } from '@json-render/react-pdf';
+
+const catalog = defineCatalog(schema, {
+  components: standardComponentDefinitions,
+});
+```
+
+## Render Functions
+
+Server-side functions for producing PDF output. All accept a spec and optional `RenderOptions`.
+
+```typescript
+import { renderToBuffer, renderToStream, renderToFile } from '@json-render/react-pdf';
+
+const buffer = await renderToBuffer(spec);
+
+const stream = await renderToStream(spec);
+stream.pipe(res);
+
+await renderToFile(spec, './output.pdf');
+```
+
+### RenderOptions
+
+```typescript
+interface RenderOptions {
+  registry?: ComponentRegistry;
+  includeStandard?: boolean;  // default: true
+  state?: Record<string, unknown>;
+}
+```
+
+<table>
+  <thead>
+    <tr>
+      <th>Option</th>
+      <th>Description</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td><code>registry</code></td>
+      <td>Custom component map (merged with standard components)</td>
+    </tr>
+    <tr>
+      <td><code>includeStandard</code></td>
+      <td>Include built-in standard components (default: <code>true</code>)</td>
+    </tr>
+    <tr>
+      <td><code>state</code></td>
+      <td>Initial state for <code>$state</code> / <code>$cond</code> dynamic prop resolution</td>
+    </tr>
+  </tbody>
+</table>
+
+## defineRegistry
+
+Create a type-safe component registry from a catalog. Components receive `{ props, children, emit, bindings, loading }`.
+
+```tsx
+import { defineRegistry } from '@json-render/react-pdf';
+import { View, Text } from '@react-pdf/renderer';
+
+const { registry } = defineRegistry(catalog, {
+  components: {
+    Badge: ({ props }) => (
+      <View style={{ backgroundColor: props.color ?? '#e5e7eb', padding: 4, borderRadius: 4 }}>
+        <Text style={{ fontSize: 10 }}>{props.label}</Text>
+      </View>
+    ),
+  },
+});
+
+const buffer = await renderToBuffer(spec, { registry });
+```
+
+## createRenderer
+
+Create a standalone renderer component wired to state, actions, and validation.
+
+```typescript
+import { createRenderer } from '@json-render/react-pdf';
+
+const PDFRenderer = createRenderer(catalog, components);
+```
+
+```typescript
+interface CreateRendererProps {
+  spec: Spec | null;
+  state?: Record<string, unknown>;
+  onAction?: (actionName: string, params?: Record<string, unknown>) => void;
+  onStateChange?: (path: string, value: unknown) => void;
+  loading?: boolean;
+  fallback?: ComponentRenderer;
+}
+```
+
+## Renderer
+
+The main component that renders a spec to `@react-pdf/renderer` elements.
+
+```typescript
+interface RendererProps {
+  spec: Spec | null;
+  registry?: ComponentRegistry;
+  includeStandard?: boolean;  // default: true
+  loading?: boolean;
+  fallback?: ComponentRenderer;
+}
+```
+
+## Standard Components
+
+### Document Structure
+
+#### Document
+
+Top-level PDF wrapper. Must be the root element. Children must be `Page` components.
+
+```typescript
+{
+  title: string | null;
+  author: string | null;
+  subject: string | null;
+}
+```
+
+#### Page
+
+A page in the document with configurable size, orientation, and margins.
+
+```typescript
+{
+  size: "A4" | "A3" | "A5" | "LETTER" | "LEGAL" | "TABLOID" | null;
+  orientation: "portrait" | "landscape" | null;
+  marginTop: number | null;
+  marginBottom: number | null;
+  marginLeft: number | null;
+  marginRight: number | null;
+  backgroundColor: string | null;
+}
+```
+
+### Layout
+
+#### View
+
+Generic container with padding, margin, background, border, and flex alignment.
+
+```typescript
+{
+  padding: number | null;
+  paddingTop: number | null;
+  paddingBottom: number | null;
+  paddingLeft: number | null;
+  paddingRight: number | null;
+  margin: number | null;
+  backgroundColor: string | null;
+  borderWidth: number | null;
+  borderColor: string | null;
+  borderRadius: number | null;
+  flex: number | null;
+  alignItems: "flex-start" | "center" | "flex-end" | "stretch" | null;
+  justifyContent: "flex-start" | "center" | "flex-end" | "space-between" | "space-around" | null;
+}
+```
+
+#### Row
+
+Horizontal flex layout with optional wrapping.
+
+```typescript
+{
+  gap: number | null;
+  alignItems: "flex-start" | "center" | "flex-end" | "stretch" | null;
+  justifyContent: "flex-start" | "center" | "flex-end" | "space-between" | "space-around" | null;
+  padding: number | null;
+  flex: number | null;
+  wrap: boolean | null;
+}
+```
+
+#### Column
+
+Vertical flex layout.
+
+```typescript
+{
+  gap: number | null;
+  alignItems: "flex-start" | "center" | "flex-end" | "stretch" | null;
+  justifyContent: "flex-start" | "center" | "flex-end" | "space-between" | "space-around" | null;
+  padding: number | null;
+  flex: number | null;
+}
+```
+
+### Content
+
+#### Heading
+
+h1-h4 heading text with configurable color and alignment.
+
+```typescript
+{
+  text: string;
+  level: "h1" | "h2" | "h3" | "h4" | null;
+  color: string | null;
+  align: "left" | "center" | "right" | null;
+}
+```
+
+#### Text
+
+Body text with full styling control.
+
+```typescript
+{
+  text: string;
+  fontSize: number | null;
+  color: string | null;
+  align: "left" | "center" | "right" | null;
+  fontWeight: "normal" | "bold" | null;
+  fontStyle: "normal" | "italic" | null;
+  lineHeight: number | null;
+}
+```
+
+#### Image
+
+Image from a URL with optional dimensions and fit.
+
+```typescript
+{
+  src: string;
+  width: number | null;
+  height: number | null;
+  objectFit: "contain" | "cover" | "fill" | "none" | null;
+}
+```
+
+#### Link
+
+Hyperlink with visible text.
+
+```typescript
+{
+  text: string;
+  href: string;
+  fontSize: number | null;
+  color: string | null;
+}
+```
+
+### Data
+
+#### Table
+
+Data table with typed columns and string rows. Supports header styling and striped rows.
+
+```typescript
+{
+  columns: { header: string; width?: string; align?: "left" | "center" | "right" }[];
+  rows: string[][];
+  headerBackgroundColor: string | null;
+  headerTextColor: string | null;
+  borderColor: string | null;
+  fontSize: number | null;
+  striped: boolean | null;
+}
+```
+
+#### List
+
+Ordered or unordered list.
+
+```typescript
+{
+  items: string[];
+  ordered: boolean | null;
+  fontSize: number | null;
+  color: string | null;
+  spacing: number | null;
+}
+```
+
+### Decorative
+
+#### Divider
+
+Horizontal line separator.
+
+```typescript
+{
+  color: string | null;
+  thickness: number | null;
+  marginTop: number | null;
+  marginBottom: number | null;
+}
+```
+
+#### Spacer
+
+Empty vertical space.
+
+```typescript
+{
+  height: number | null;
+}
+```
+
+### Page-Level
+
+#### PageNumber
+
+Renders current page number and total pages. Format uses `{pageNumber}` and `{totalPages}` placeholders.
+
+```typescript
+{
+  format: string | null;    // default: "{pageNumber} / {totalPages}"
+  fontSize: number | null;
+  color: string | null;
+  align: "left" | "center" | "right" | null;
+}
+```
+
+## Server-Safe Import
+
+Import schema and catalog definitions without pulling in React or `@react-pdf/renderer`:
+
+```typescript
+import { schema, standardComponentDefinitions } from '@json-render/react-pdf/server';
+```
+
+## Sub-path Exports
+
+<table>
+  <thead>
+    <tr>
+      <th>Export</th>
+      <th>Description</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td><code>@json-render/react-pdf</code></td>
+      <td>Full package: schema, renderer, components, render functions</td>
+    </tr>
+    <tr>
+      <td><code>@json-render/react-pdf/server</code></td>
+      <td>Schema and catalog definitions only (no React)</td>
+    </tr>
+    <tr>
+      <td><code>@json-render/react-pdf/catalog</code></td>
+      <td>Standard component definitions and types</td>
+    </tr>
+    <tr>
+      <td><code>@json-render/react-pdf/render</code></td>
+      <td>Server-side render functions only</td>
+    </tr>
+  </tbody>
+</table>
+
+## Types
+
+<table>
+  <thead>
+    <tr>
+      <th>Export</th>
+      <th>Description</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td><code>ReactPdfSchema</code></td>
+      <td>Schema type for PDF specs</td>
+    </tr>
+    <tr>
+      <td><code>ReactPdfSpec</code></td>
+      <td>Spec type for PDF documents</td>
+    </tr>
+    <tr>
+      <td><code>RenderOptions</code></td>
+      <td>Options for render functions</td>
+    </tr>
+    <tr>
+      <td><code>ComponentContext</code></td>
+      <td>Typed component render function context</td>
+    </tr>
+    <tr>
+      <td><code>ComponentFn</code></td>
+      <td>Component render function type</td>
+    </tr>
+    <tr>
+      <td><code>StandardComponentDefinitions</code></td>
+      <td>Type of the standard component definitions object</td>
+    </tr>
+    <tr>
+      <td><code>StandardComponentProps&lt;K&gt;</code></td>
+      <td>Inferred props type for a standard component by name</td>
+    </tr>
+  </tbody>
+</table>

+ 15 - 4
apps/web/app/globals.css

@@ -154,23 +154,34 @@ button {
   margin-bottom: 0.5em;
 }
 
-/* MDX table styles — fallback for GFM-generated tables */
+/* MDX table styles — applies to both GFM pipe tables and raw HTML tables */
 .mdx-table th,
-.mdx-table td {
+.mdx-table td,
+article table th,
+article table td {
   border: 1px solid var(--border);
   padding: 0.75rem 1rem;
   text-align: left;
 }
 
-.mdx-table th {
+.mdx-table th,
+article table th {
   font-weight: 600;
   background-color: var(--muted);
 }
 
-.mdx-table td {
+.mdx-table td,
+article table td {
   color: var(--muted-foreground);
 }
 
+article table {
+  width: 100%;
+  font-size: 0.875rem;
+  border-collapse: collapse;
+  margin: 1.5rem 0;
+}
+
 /* Shiki dual theme support */
 .shiki,
 .shiki span {

+ 6 - 0
apps/web/lib/docs-navigation.ts

@@ -57,6 +57,11 @@ export const docsNavigation: NavSection[] = [
         href: "https://github.com/vercel-labs/json-render/tree/main/examples/react-native",
         external: true,
       },
+      {
+        title: "React PDF",
+        href: "https://github.com/vercel-labs/json-render/tree/main/examples/react-pdf",
+        external: true,
+      },
       {
         title: "Remotion",
         href: "https://github.com/vercel-labs/json-render/tree/main/examples/remotion",
@@ -86,6 +91,7 @@ export const docsNavigation: NavSection[] = [
     items: [
       { title: "@json-render/core", href: "/docs/api/core" },
       { title: "@json-render/react", href: "/docs/api/react" },
+      { title: "@json-render/react-pdf", href: "/docs/api/react-pdf" },
       { title: "@json-render/shadcn", href: "/docs/api/shadcn" },
       { title: "@json-render/react-native", href: "/docs/api/react-native" },
       { title: "@json-render/remotion", href: "/docs/api/remotion" },

+ 1 - 0
apps/web/lib/page-titles.ts

@@ -38,6 +38,7 @@ export const PAGE_TITLES: Record<string, string> = {
   // API references
   "docs/api/core": "@json-render/core API",
   "docs/api/react": "@json-render/react API",
+  "docs/api/react-pdf": "@json-render/react-pdf API",
   "docs/api/react-native": "@json-render/react-native API",
   "docs/api/codegen": "@json-render/codegen API",
   "docs/api/remotion": "@json-render/remotion API",