npm package: @io-reactive/router
Description: Reactive router for IO framework - powerful, lightweight, and fully typed routing solution for modern SPA applications
Author: Alexei Kohanovich
Latest version: 1.0.15

# io-reactive-router 🚀

**Reactive router for IO-Reactive framework** – a powerful, lightweight, and fully typed router for building modern SPA applications.

[![npm version](https://badge.fury.io/js/io-reactive-router.svg)](https://badge.fury.io/js/io-reactive-router)
[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## ✨ Features

-   🚀 **Lightweight** – Minimal bundle size
-   ⚡ **Fast** – Optimized for maximum performance
-   🔄 **Reactive** – Automatic updates on state change
-   📱 **SPA Ready** – Full single-page application support
-   🎨 **Animations** – Built-in page transitions
-   🔗 **Smart Links** – Automatic active state management
-   📊 **Nested Routes** – Layout and nested routing support
-   🔍 **Query Parameters** – Work with URL parameters
-   🍞 **Breadcrumbs** – Automatic breadcrumbs generation
-   🛡️ **Guards** – Route protection and middleware
-   🗂️ **Caching** – Optimized page caching
-   🔍 **SEO** – Page metadata management

## 📦 Installation

```bash
# First, install the core framework
npm install io-reactive

# Then install the router
npm install io-reactive-router
```

## 🚀 Quick Start

```typescript
import { IO } from 'io-reactive';
import { IORouter, Route } from 'io-reactive-router';

// Define routes
const routes = [
    {
        path: '/',
        template: () => new IO('div', { text: 'Home Page' }),
        name: 'home',
    },
    {
        path: '/about',
        template: () => new IO('div', { text: 'About' }),
        name: 'about',
    },
    {
        path: '/users/[id]',
        template: (params) => new IO('div', { text: `User ID: ${params.id}` }),
        name: 'user',
    },
];

// Initialize the router
const router = new IORouter({
    routes: Promise.resolve(routes),
    domain: 'https://myapp.com',
    root: document.getElementById('app'),
});

// Start the router
router.init();
```

## 📚 Documentation

-   **[Islands architecture](https://github.com/alexeykoh/IO/tree/main/packages/io-reactive/docs/islands-architecture.md)**
-   [API Reference](#api-reference)
-   [Examples](https://github.com/alexeykoh/IO/tree/main/packages/demo-site)

## 🛣️ Defining Routes

### Simple Routes

```typescript
const routes = [
    {
        path: '/',
        template: () => new IO('div', { text: 'Home' }),
        name: 'home',
    },
    {
        path: '/products',
        template: () => new IO('div', { text: 'Products' }),
        name: 'products',
    },
];
```

### Dynamic Routes

```typescript
const routes = [
    {
        path: '/users/[id]',
        template: (params) =>
            new IO('div', {
                text: `User: ${params.id}`,
            }),
        name: 'user',
    },
    {
        path: '/posts/[slug]/comments/[commentId]',
        template: (params) =>
            new IO('div', {
                text: `Post: ${params.slug}, Comment: ${params.commentId}`,
            }),
        name: 'comment',
    },
];
```

### Nested Routes

```typescript
const routes = [
    {
        path: '/admin',
        template: () => new IO('div', { text: 'Admin Panel' }),
        name: 'admin',
        children: [
            {
                path: '/users',
                template: () => new IO('div', { text: 'User Management' }),
                name: 'admin-users',
            },
            {
                path: '/settings',
                template: () => new IO('div', { text: 'Settings' }),
                name: 'admin-settings',
            },
        ],
    },
];
```

### Protected Routes

```typescript
const routes = [
    {
        path: '/profile',
        template: () => new IO('div', { text: 'Profile' }),
        name: 'profile',
        params: {
            isPrivate: true, // Requires authentication
            redirectTo: '/login', // Redirect for unauthenticated
        },
    },
];
```

## 🎛️ Router Configuration

```typescript
const router = new IORouter({
    routes: Promise.resolve(routes),
    domain: 'https://myapp.com',
    root: document.getElementById('app'),

    // Auth function
    auth: async () => {
        const token = localStorage.getItem('authToken');
        return !!token;
    },

    // Layout for all pages
    layout: (components) =>
        new IO('div', {
            classList: ['app-layout'],
            components: [
                () => new IO('header', { text: 'Header' }),
                ...components,
                () => new IO('footer', { text: 'Footer' }),
            ],
        }),

    // Middleware for each route
    middleware: ({ domain, routes, href, state }) => {
        console.log('Navigating to:', href);
        // Analytics, logging, etc.
    },
});
```

## 🔗 Navigation

### Programmatic Navigation

```typescript
// Simple navigation
router.navigate('/about');

// Navigation with query params
router.navigate('/products', {
    query: { category: 'electronics', page: '1' },
});

// Navigation with state
router.navigate('/profile', {
    state: { fromPage: 'dashboard' },
    replace: true, // Replace current history entry
});
```

### History Navigation

```typescript
// Back
router.history('back');

// Forward
router.history('next');
```

## 🧩 Components

### IOActiveLink – Smart Links

Component for creating links with automatic active state management:

```typescript
import { IOActiveLink } from 'io-reactive-router';

const navLink = new IOActiveLink('/products', {
    text: 'Products',
    activeClass: 'active',
    exactMatch: true, // Exact path match
    prefetch: true, // Prefetch page
    attributes: {
        'data-analytics': 'nav-products',
    },
});
```

**IOActiveLink options:**

-   `activeClass` – CSS class for active state
-   `exactMatch` – Exact path match (default: false)
-   `prefetch` – Prefetch page on hover
-   `attributes` – Additional link attributes

### IOLayout – Layout Component

Component for creating layouts with nested route support:

```typescript
import { IOLayout } from 'io-reactive-router';

const layout = new IOLayout({
    fallback: () =>
        new IO('div', {
            text: 'Loading...',
            classList: ['loading'],
        }),
    depth: 1, // Nesting depth
    animations: {
        enter: 'fadeIn',
        exit: 'fadeOut',
    },
});
```

**Factory methods:**

```typescript
// Simple layout
const simpleLayout = createIOLayout({
    header: () => new IO('header', { text: 'Header' }),
    footer: () => new IO('footer', { text: 'Footer' }),
});

// Layout with animations
const animatedLayout = createAnimatedIOLayout({
    animation: 'slide',
    duration: 300,
});
```

## 🔧 Modules

### 📊 Query Parameters

Work with URL parameters:

```typescript
// Get parameters
const queryParams = router.queryParams();
console.log(queryParams()); // { page: '1', filter: 'active' }

// Programmatic control
import { QueryParamsModule } from 'io-reactive-router';

const queryModule = new QueryParamsModule('https://myapp.com');

// Set parameters
queryModule.set({ page: '2', sort: 'name' });

// Get parameters
const params = queryModule.get(); // { page: '2', sort: 'name' }

// Remove parameter
queryModule.remove('sort');
```

### 🍞 Breadcrumbs

Automatic breadcrumbs generation:

```typescript
// Get breadcrumbs
const breadcrumbs = router.breadcrumbs();

// Breadcrumb structure
interface Breadcrumb {
    path: string;
    label: string;
    isActive: boolean;
}

// Use in a component
const breadcrumbsComponent = new IO('nav', {
    components: breadcrumbs.map(
        (crumb) => () =>
            new IOActiveLink(crumb.path, {
                text: crumb.label,
                activeClass: 'breadcrumb-active',
            })
    ),
});
```

### 🎨 Route Animations

#### Simple Animations

```typescript
import { RouteAnimationModule, FadeAnimation } from 'io-reactive-router';

const animationModule = new RouteAnimationModule(document.getElementById('app'));

// Set animation strategy
animationModule.setAnimationStrategy(
    new FadeAnimation({
        duration: 300,
        easing: 'ease-in-out',
    })
);
```

#### Layout Animations

```typescript
import { LayoutAnimationModule } from 'io-reactive-router';

const layoutModule = new LayoutAnimationModule(document.getElementById('app'));

// Set animation for a specific level
router.setLayoutAnimationStrategy(
    1,
    new FadeAnimation({
        duration: 500,
    })
);
```

#### Custom Animations

```typescript
import { AnimationStrategy } from 'io-reactive-router';

class CustomSlideAnimation implements AnimationStrategy {
    async enter(element: HTMLElement): Promise<void> {
        element.style.transform = 'translateX(100%)';
        element.style.transition = 'transform 0.3s ease';

        await new Promise((resolve) => {
            setTimeout(() => {
                element.style.transform = 'translateX(0)';
                setTimeout(resolve, 300);
            }, 10);
        });
    }

    async exit(element: HTMLElement): Promise<void> {
        element.style.transform = 'translateX(-100%)';
        await new Promise((resolve) => setTimeout(resolve, 300));
    }
}
```

### 🛡️ Guards

Route protection with guards:

```typescript
import { RouteGuard } from 'io-reactive-router';

// Create a guard
const authGuard: RouteGuard = async (to, from, next) => {
    const isAuthenticated = await checkAuth();

    if (isAuthenticated) {
        next(); // Allow navigation
    } else {
        next('/login'); // Redirect to login
    }
};

// Add guard for a specific route
router.addRouteGuard('/profile', authGuard);

// Global guard for all routes
router.addGlobalGuard(authGuard);
```

### 🗂️ Caching

Performance optimization via caching:

```typescript
// Caching is enabled by default
// Prefetch a page
await router.prefetch('/products');

// Clear cache
router.clearCache();
```

### 🔍 SEO & Metadata

Manage page metadata:

```typescript
// Set metadata for a route
router.setRouteMetadata('/products', {
    title: 'Our Products - Online Store',
    description: 'Wide selection of quality products at affordable prices',
    keywords: ['products', 'online store', 'shopping'],
    openGraph: {
        title: 'Our Products',
        description: 'Best products for you',
        image: 'https://myapp.com/og-products.jpg',
        url: 'https://myapp.com/products',
    },
    twitter: {
        card: 'summary_large_image',
        title: 'Our Products',
        description: 'Best products for you',
    },
});

// Get current metadata
const currentMeta = router.getCurrentMetadata();
```

## 🎯 Advanced Features

### Lazy Loading Routes

```typescript
const routes = [
    {
        path: '/dashboard',
        template: () => import('./pages/Dashboard').then((m) => m.Dashboard),
        name: 'dashboard',
    },
];
```

### Multiple Layouts

```typescript
const adminLayout = (components) =>
    new IO('div', {
        classList: ['admin-layout'],
        components: [() => new IO('aside', { text: 'Admin Menu' }), () => new IO('main', { components })],
    });

const publicLayout = (components) =>
    new IO('div', {
        classList: ['public-layout'],
        components: [() => new IO('header', { text: 'Public Header' }), () => new IO('main', { components })],
    });

// Using different layouts
const routes = [
    {
        path: '/admin',
        template: () => new IO('div', { text: 'Admin' }),
        layout: adminLayout,
    },
    {
        path: '/',
        template: () => new IO('div', { text: 'Home' }),
        layout: publicLayout,
    },
];
```

### Navigation State

```typescript
// Get navigation state
const navState = router.getNavigationState();

// Pass state during navigation
router.navigate('/profile', {
    state: {
        previousPage: 'dashboard',
        userAction: 'edit',
    },
});
```

## 📋 API Reference

### IORouter

#### Constructor

```typescript
new IORouter(config: iIORouter)
```

#### Methods

-   `init()` – Initialize the router
-   `navigate(path, options?)` – Programmatic navigation
-   `history(direction)` – History navigation
-   `breadcrumbs()` – Get breadcrumbs
-   `queryParams()` – Work with query parameters
-   `addRouteGuard(path, guard)` – Add guard for a route
-   `addGlobalGuard(guard)` – Add global guard
-   `setRouteMetadata(path, metadata)` – Set metadata
-   `getCurrentMetadata()` – Get current metadata
-   `getCurrentRoute()` – Get current route
-   `getNavigationState()` – Get navigation state
-   `prefetch(path)` – Prefetch page

### Route

#### Interface

```typescript
interface iRoute {
    path: string;
    template: routeIO;
    name: string;
    params?: {
        isPrivate?: boolean;
        redirectTo?: string;
    };
    children?: iRoute[];
}
```

## 🔧 TypeScript Support

Full TypeScript support with strict typing:

```typescript
import type {
    iIORouter,
    iRoute,
    iRoutes,
    path,
    routeIO,
    AnimationStrategy,
    RouteGuard,
    RouteMetadata,
} from 'io-reactive-router';

// Typed routes
const routes: iRoute[] = [
    {
        path: '/',
        template: () => new IO('div', { text: 'Home' }),
        name: 'home',
    },
];

// Typed config
const config: iIORouter = {
    routes: Promise.resolve(routes),
    domain: 'https://example.com',
};
```

## 🎨 Styling

### CSS Classes for Animations

```css
/* Fade animations */
.route-enter {
    opacity: 0;
}

.route-enter-active {
    opacity: 1;
    transition: opacity 0.3s ease;
}

.route-exit {
    opacity: 1;
}

.route-exit-active {
    opacity: 0;
    transition: opacity 0.3s ease;
}

/* Slide animations */
.route-slide-enter {
    transform: translateX(100%);
}

.route-slide-enter-active {
    transform: translateX(0);
    transition: transform 0.3s ease;
}

.route-slide-exit-active {
    transform: translateX(-100%);
    transition: transform 0.3s ease;
}
```

### Active Links

```css
.nav-link {
    color: #666;
    text-decoration: none;
    transition: color 0.2s ease;
}

.nav-link.active {
    color: #007bff;
    font-weight: bold;
}

.nav-link:hover {
    color: #0056b3;
}
```

## 🚀 Performance

### Optimizations

1. **Lazy Loading** – Load components on demand
2. **Prefetching** – Prefetch critical pages
3. **Caching** – Cache rendered components
4. **Tree Shaking** – Remove unused code

### Recommendations

-   Use `prefetch` for critical pages
-   Apply lazy loading for rarely used routes
-   Optimize animations for better performance
-   Use memoization in heavy components

## 🔍 Debugging

### Enable Debug Mode

```typescript
// In development mode
if (process.env.NODE_ENV === 'development') {
    // Log navigation
    window.addEventListener('io:navigate', (event) => {
        console.log('Navigation:', event.detail);
    });
}
```

### Developer Tools

```typescript
// Access router from browser console
declare global {
    interface Window {
        ioRouter: IORouter;
    }
}

// In browser console
window.ioRouter.navigate('/debug');
console.log(window.ioRouter.getCurrentRoute());
```

## 📈 Migration

### From React Router

```typescript
// React Router
<Route path="/users/:id" component={UserPage} />

// IO-Router
{
    path: '/users/[id]',
    template: (params) => new UserPage(params.id),
    name: 'user'
}
```

### From Vue Router

```typescript
// Vue Router
{ path: '/user/:id', component: User }

// IO-Router
{
    path: '/users/[id]',
    template: (params) => new User(params.id),
    name: 'user'
}
```

## 🤝 Contributing

We welcome contributions! Please see the [contributing guide](CONTRIBUTING.md).

## 📄 License

MIT © [Alexey Koh](https://github.com/alexeykoh)

## 🔗 Links

-   [IO-Reactive Documentation](https://github.com/alexeykoh/IO/tree/main/packages/io-reactive)
-   [Usage Examples](https://github.com/alexeykoh/IO/tree/main/packages/demo-site)
-   [Create App](https://github.com/alexeykoh/IO/tree/main/packages/create-io-app)

---

**Build modern web apps with io-reactive-router! 🚀**

## Публикация пакета и типы

### Как опубликовать новую версию

1. Увеличьте версию:
   ```bash
   npm version patch --no-git-tag-version --workspace=packages/io-reactive-router
   ```
2. Соберите пакет:
   ```bash
   npm run build -w io-reactive-router
   ```
3. Проверьте, что в dist/ есть .js и .d.ts:
   ```bash
   ls packages/io-reactive-router/dist/
   ```
4. Опубликуйте:
   ```bash
   npm publish --access public --workspace=packages/io-reactive-router
   ```

### Как работают типы
- Все типы автоматически подтягиваются через зависимость @oi-reactive/shared-types.
- Пользователю не нужно явно устанавливать shared-types — типы доступны "из коробки" после установки io-reactive-router.