npm package: @pooarasu/flow-builder
Description: Reusable production-ready standalone React flow builder for React 18+ apps
Author: Your Team
Latest version: 1.0.2

# `@pooarasu/flow-builder`

Reusable, production-ready standalone flow builder package for React 18+ applications.

## Features

- Fully reusable `<FlowBuilder />`
- Controlled and uncontrolled graph modes
- Strongly typed generic nodes/edges (`TypeScript`, no `any`)
- Internal standalone state engine + undo/redo history
- Node operations: add/delete/duplicate/resize/toolbar/update data
- Edge operations: connect/delete/custom type/labels/animated edges
- Canvas tools: minimap/controls/background/fit/zoom/snap/pan/multi-select
- Keyboard shortcuts: `Delete`, `Ctrl/Cmd+C`, `Ctrl/Cmd+V`, `Ctrl/Cmd+Z`, `Ctrl/Cmd+Y`, `Ctrl/Cmd+A`
- JSON import/export + localStorage save/restore
- Auto layout via built-in layout engine
- Readonly mode
- Dark/light theme
- Custom toolbar and custom context menu
- Plugin system
- ESM + CJS builds + declaration files
- Tree-shakable output

## Install

```bash
npm install @pooarasu/flow-builder react react-dom
```

## Basic Usage (Uncontrolled)

```tsx
import { FlowBuilder, type FlowNode } from "@pooarasu/flow-builder";

interface NodeData extends Record<string, unknown> {
  label: string;
}

const initialNodes: FlowNode<NodeData>[] = [
  {
    id: "a",
    type: "flowBuilderNode",
    position: { x: 100, y: 100 },
    data: { label: "Start" }
  }
];

export const Screen = () => (
  <FlowBuilder<NodeData>
    defaultNodes={initialNodes}
    defaultEdges={[]}
    snapToGrid
    persistKey="my-flow"
  />
);
```

## Controlled Usage

```tsx
import { useState } from "react";
import { FlowBuilder, type FlowEdge, type FlowNode } from "@pooarasu/flow-builder";

interface NodeData extends Record<string, unknown> {
  label: string;
}

export const ControlledFlow = () => {
  const [nodes, setNodes] = useState<FlowNode<NodeData>[]>([
    {
      id: "1",
      type: "flowBuilderNode",
      position: { x: 150, y: 120 },
      data: { label: "Node 1" }
    }
  ]);
  const [edges, setEdges] = useState<FlowEdge[]>([]);

  return (
    <FlowBuilder<NodeData>
      nodes={nodes}
      edges={edges}
      onNodesChange={(_, nextNodes) => setNodes(nextNodes)}
      onEdgesChange={(_, nextEdges) => setEdges(nextEdges)}
      onNodeAdd={(node) => setNodes((prev) => [...prev, node])}
      onNodeDelete={(deleted) =>
        setNodes((prev) => prev.filter((entry) => !deleted.some((d) => d.id === entry.id)))
      }
      onNodeDuplicate={(_, duplicated) => setNodes((prev) => [...prev, ...duplicated])}
      onConnect={(_, edge) => {
        if (!edge) return;
        setEdges((prev) => [...prev, edge]);
      }}
      onEdgeDelete={(deleted) =>
        setEdges((prev) => prev.filter((edge) => !deleted.some((d) => d.id === edge.id)))
      }
    />
  );
};
```

## Advanced Example

```tsx
<FlowBuilder
  theme="dark"
  readonly={false}
  snapToGrid
  snapGrid={[20, 20]}
  persistKey="flow-state"
  validateNode={(node) => Boolean(node.data)}
  validateConnection={(connection) => connection.source !== connection.target}
  renderToolbar={({ api }) => (
    <button type="button" onClick={() => api.autoLayout("LR")}>
      Horizontal Layout
    </button>
  )}
  renderContextMenu={({ menu, api, close }) => (
    <div style={{ position: "absolute", left: menu.x, top: menu.y }}>
      <button
        type="button"
        onClick={() => {
          api.addNode({ position: { x: menu.x, y: menu.y } });
          close();
        }}
      >
        Add node here
      </button>
    </div>
  )}
/>
```

## Plugin Example

```tsx
import { createFlowPlugin } from "@pooarasu/flow-builder";

const auditPlugin = createFlowPlugin({
  id: "audit",
  onEvent(event, api) {
    if (event.type === "node:add") {
      api.updateNodeData(event.node.id, (prev) => ({
        ...prev,
        createdAt: new Date().toISOString()
      }));
    }
  }
});

<FlowBuilder plugins={[auditPlugin]} />;
```

## Public API

### Components

- `FlowBuilder`
- `FlowProvider`

### Hooks

- `useFlow()`
- `useFlowHistory()`
- `useFlowSelection()`

### Core Props

```tsx
<FlowBuilder
  nodes={nodes}
  edges={edges}
  onNodesChange={fn}
  onEdgesChange={fn}
  onConnect={fn}
  onNodeAdd={fn}
  onNodeDelete={fn}
  onEdgeDelete={fn}
  readonly={false}
  theme="light"
  plugins={[...]}
/>
```

## Styling

- Default styles are isolated under `.rfb-*` class names.
- Import from package root includes styles automatically.
- Optional explicit style import:

```ts
import "@pooarasu/flow-builder/styles.css";
```

- Theme override example:

```css
.my-flow-wrapper {
  --rfb-primary: #2563eb;
  --rfb-node-bg: #ffffff;
}
```

## Folder Structure

```txt
src/
  components/
  nodes/
  edges/
  hooks/
  store/
  utils/
  types/
  plugins/
  styles/
examples/
  basic/
tests/
```

## Development

```bash
npm install
npm run typecheck
npm run test
npm run build
```

Run example app:

```bash
npm install --prefix examples/basic
npm run example:dev
```

## Build Output

- ESM: `dist/index.js`
- CJS: `dist/index.cjs`
- Types: `dist/index.d.ts`
- CSS: `dist/styles.css`

## Publish To npm

1. Update package metadata in `package.json` (`name`, `version`, `author`).
2. Login:
   ```bash
   npm login
   ```
3. Validate:
   ```bash
   npm run prepublishOnly
   ```
4. Publish:
   ```bash
   npm publish --access public
   ```

## Deployment Notes (Vercel / Netlify / CRA / Vite)

- Keep `react` and `react-dom` as peer dependencies in published package.
- Consumer app should install peers explicitly.
- Library works in:
  - Vercel-hosted React apps
  - Netlify-hosted React apps
  - CRA consumers
  - Vite consumers
- For SSR apps, render `FlowBuilder` on client only (it uses browser APIs like pointer events and ResizeObserver).

## Scripts

- `npm run dev` - local package dev
- `npm run build` - bundle library
- `npm run test` - run tests
- `npm run typecheck` - strict TS check
- `npm run example:dev` - run example app