npm package: @kiiako/render
Description: React renderer for Kiiako blocks
Author: Kiiako Team
Latest version: 1.1.0

# @kiiako/render

React renderer for Kiiako blocks - A lightweight, flexible library for rendering dynamic page blocks in your React applications.

## Installation

```bash
npm install @kiiako/render
```

```bash
yarn add @kiiako/render
```

```bash
pnpm add @kiiako/render
```

## Features

- 🚀 **Lightweight** - Minimal dependencies, only React as peer dependency
- 🎨 **Flexible** - Register custom blocks and components
- 📦 **TypeScript** - Full TypeScript support with type definitions
- 🔧 **Customizable** - Support for custom styles, animations, and visibility rules
- 🎯 **Simple API** - Easy to use and integrate

## Quick Start

### 1. Register your blocks

```tsx
import { categoryRegistry } from '@kiiako/render';

// Define your React component
function HeroBlock({ title, subtitle }: { title: string; subtitle: string }) {
  return (
    <div className="hero">
      <h1>{title}</h1>
      <p>{subtitle}</p>
    </div>
  );
}

// Register a category
categoryRegistry.registerCategory({
  id: 'hero',
  name: 'hero',
  label: 'Hero Sections',
  order: 1,
  blocks: [],
  enabled: true,
});

// Register the block
categoryRegistry.registerBlock('hero', {
  id: 'hero-simple',
  category: 'hero',
  name: 'Simple Hero',
  label: 'Simple Hero',
  description: 'A simple hero section',
  component: HeroBlock,
  defaultProps: {
    title: 'Welcome',
    subtitle: 'This is a hero section',
  },
});
```

### 2. Render blocks

```tsx
import { BlockRenderer, PageRenderer } from '@kiiako/render';
import type { BlockInstance } from '@kiiako/render';

// Render a single block
function SingleBlock() {
  const block: BlockInstance = {
    id: 'hero-simple',
    instanceId: 'block-1',
    type: 'block',
    category: 'hero',
    name: 'Simple Hero',
    label: 'Simple Hero',
    props: {
      title: 'Hello World',
      subtitle: 'Welcome to my site',
    },
    order: 0,
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
  };

  return <BlockRenderer block={block} />;
}

// Render multiple blocks (a page)
function Page() {
  const blocks: BlockInstance[] = [
    // ... your blocks
  ];

  return <PageRenderer blocks={blocks} />;
}
```

## API Reference

### Components

#### `BlockRenderer`

Renders a single block instance.

```tsx
<BlockRenderer block={blockInstance} />
```

**Props:**
- `block` (BlockInstance): The block instance to render

#### `PageRenderer`

Renders a list of blocks (typically a page).

```tsx
<PageRenderer blocks={blocks} className="my-page" />
```

**Props:**
- `blocks` (BlockInstance[]): Array of block instances to render
- `className` (string, optional): CSS class name for the container

### Registries

#### `categoryRegistry`

Manage block categories.

**Methods:**
- `registerCategory(category: Category)` - Register a new category
- `registerBlock(categoryId: string, block: BlockDefinition)` - Register a block to a category
- `getBlock(blockId: string)` - Get a block definition by ID
- `getAllBlocks()` - Get all registered blocks
- `getCategory(id: string)` - Get a category by ID
- `getAllCategories()` - Get all registered categories

#### `componentRegistry`

Manage atomic components (reusable components that aren't categorized blocks).

**Methods:**
- `registerComponent(component: ComponentDefinition)` - Register a component
- `getComponent(id: string)` - Get a component by ID
- `getAllComponents()` - Get all registered components

### Types

#### `BlockInstance`

```typescript
interface BlockInstance {
  id: string;
  instanceId: string;
  type: 'block' | 'component';
  category: string;
  name: string;
  label: string;
  props: Record<string, any>;
  order: number;
  createdAt: string;
  updatedAt: string;
  // Optional properties
  style?: Record<string, any>;
  attributes?: {
    id?: string;
    className?: string;
    [key: string]: any;
  };
  customCss?: string;
  animation?: {
    name?: string;
    duration?: number;
    delay?: number;
  };
  visibility?: {
    mobile?: boolean;
    tablet?: boolean;
    desktop?: boolean;
  };
  dataAttributes?: Record<string, string>;
}
```

#### `BlockDefinition`

```typescript
interface BlockDefinition {
  id: string;
  category: string;
  name: string;
  label: string;
  component: React.ComponentType<any>;
  defaultProps: Record<string, any>;
  description?: string;
  icon?: string;
  thumbnail?: string;
  previewImage?: string;
  tags?: string[];
}
```

## Advanced Usage

### Custom Styling

Blocks support multiple ways to apply styles:

```tsx
const block: BlockInstance = {
  // ... other properties
  style: {
    backgroundColor: '#f0f0f0',
    padding: '20px',
  },
  attributes: {
    className: 'my-custom-class',
  },
  customCss: `
    &:hover {
      transform: scale(1.05);
    }
  `,
};
```

### Animations

Add animations to blocks:

```tsx
const block: BlockInstance = {
  // ... other properties
  animation: {
    name: 'fadeIn',
    duration: 500,
    delay: 200,
  },
};
```

### Responsive Visibility

Control block visibility across devices:

```tsx
const block: BlockInstance = {
  // ... other properties
  visibility: {
    mobile: true,
    tablet: true,
    desktop: false, // Hidden on desktop
  },
};
```

### Data Attributes

Add custom data attributes:

```tsx
const block: BlockInstance = {
  // ... other properties
  dataAttributes: {
    'tracking-id': 'hero-section',
    'category': 'hero',
  },
};
// Renders as: data-tracking-id="hero-section" data-category="hero"
```

## Example: Complete Integration

```tsx
import { categoryRegistry, PageRenderer } from '@kiiako/render';
import type { BlockInstance } from '@kiiako/render';

// 1. Define your components
function HeroComponent({ title, subtitle }: any) {
  return (
    <section className="hero">
      <h1>{title}</h1>
      <p>{subtitle}</p>
    </section>
  );
}

function FeatureComponent({ title, description, icon }: any) {
  return (
    <div className="feature">
      <span>{icon}</span>
      <h3>{title}</h3>
      <p>{description}</p>
    </div>
  );
}

// 2. Register blocks
categoryRegistry.registerCategory({
  id: 'sections',
  name: 'sections',
  label: 'Sections',
  order: 1,
  blocks: [],
  enabled: true,
});

categoryRegistry.registerBlock('sections', {
  id: 'hero-simple',
  category: 'sections',
  name: 'Hero',
  label: 'Hero Section',
  component: HeroComponent,
  defaultProps: { title: '', subtitle: '' },
});

categoryRegistry.registerBlock('sections', {
  id: 'feature',
  category: 'sections',
  name: 'Feature',
  label: 'Feature Block',
  component: FeatureComponent,
  defaultProps: { title: '', description: '', icon: '✨' },
});

// 3. Create your page with blocks
function MyPage() {
  const blocks: BlockInstance[] = [
    {
      id: 'hero-simple',
      instanceId: 'hero-1',
      type: 'block',
      category: 'sections',
      name: 'Hero',
      label: 'Hero Section',
      props: {
        title: 'Welcome to Kiiako',
        subtitle: 'Build amazing pages with blocks',
      },
      order: 0,
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    },
    {
      id: 'feature',
      instanceId: 'feature-1',
      type: 'block',
      category: 'sections',
      name: 'Feature',
      label: 'Feature Block',
      props: {
        title: 'Easy to Use',
        description: 'Simple API and great documentation',
        icon: '🚀',
      },
      order: 1,
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    },
  ];

  return <PageRenderer blocks={blocks} className="container" />;
}
```

## Loading Blocks from API

```tsx
import { useEffect, useState } from 'react';
import { PageRenderer } from '@kiiako/render';
import type { BlockInstance } from '@kiiako/render';

function DynamicPage({ pageSlug }: { pageSlug: string }) {
  const [blocks, setBlocks] = useState<BlockInstance[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(`/api/pages/${pageSlug}`)
      .then((res) => res.json())
      .then((data) => {
        setBlocks(data.blocks);
        setLoading(false);
      });
  }, [pageSlug]);

  if (loading) return <div>Loading...</div>;

  return <PageRenderer blocks={blocks} />;
}
```

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## License

MIT © Kiiako Team

## Support

For issues and questions, please visit [GitHub Issues](https://github.com/kiiako/render/issues)