Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/connection-validation-and-reactflow-props.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflowbuilder/sdk': minor
---

feat: add `isValidConnection` (validate connections as the user draws them) and `reactFlowProps` (forward extra props to the ReactFlow canvas) to `<WorkflowBuilder.Root>`.
17 changes: 16 additions & 1 deletion apps/demo/src/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { WorkflowBuilder } from '@workflowbuilder/sdk';
import type { WorkflowBuilderEdgeTemplates, WorkflowBuilderNodeTemplates } from '@workflowbuilder/sdk';
import type {
WorkflowBuilderEdgeTemplates,
WorkflowBuilderIsValidConnection,
WorkflowBuilderNodeTemplates,
WorkflowBuilderReactFlowProps,
} from '@workflowbuilder/sdk';

import '@workflowbuilder/sdk/style.css';

Expand Down Expand Up @@ -30,6 +35,14 @@ const edgeTemplates = {
dashed: DashedEdge,
} satisfies WorkflowBuilderEdgeTemplates;

// A trigger is a workflow entry point, so it can never be a connection target.
const isValidConnection: WorkflowBuilderIsValidConnection = ({ targetNode }) => targetNode.data.type !== 'trigger';

// Advanced escape hatch: forward extra ReactFlow props (SDK-owned props can't be set here).
const reactFlowProps = {
zoomOnDoubleClick: false,
} satisfies WorkflowBuilderReactFlowProps;

export function App() {
return (
<WorkflowBuilder.Root
Expand All @@ -38,6 +51,8 @@ export function App() {
nodeTemplates={nodeTemplates}
edgeTemplates={edgeTemplates}
diagramTemplates={demoTemplates}
isValidConnection={isValidConnection}
reactFlowProps={reactFlowProps}
plugins={[
demoPlugin,
analyticsPlugin,
Expand Down
59 changes: 47 additions & 12 deletions apps/docs/src/content/docs/guides/configuring-the-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ import { WorkflowBuilder } from '@workflowbuilder/sdk';

All props optional unless marked.

| Prop | Type | Default | Description |
| ------------------ | -------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------------------------------- |
| `integration` | [`WorkflowBuilderIntegration`](#integration-strategies) | `{ strategy: 'localStorage' }` | How the builder loads and persists diagram data. |
| `nodeTypes` | [`PaletteItemOrGroup[]`](#node-types) | `[]` | Node type definitions rendered in the palette and used for validation. |
| `diagramTemplates` | `TemplateModel[]` | `[]` | Diagram templates available in the template selector. |
| `jsonForm` | [`WorkflowBuilderJsonFormConfig`](/guides/custom-jsonforms-control/) | — | Custom JSONForms renderers, cells, and translations. |
| `plugins` | [`WorkflowBuilderPlugin[]`](/guides/build-a-plugin/) | — | Plugin initializer functions. Each called once on first mount. |
| `name` | `string` | — | Workflow name displayed in the header. |
| `layoutDirection` | `'DOWN' \| 'RIGHT'` | `'DOWN'` | Flow direction of the diagram. |
| `initialNodes` | `WorkflowBuilderNode[]` | `[]` | Initial diagram nodes (used by the `props` integration strategy). |
| `initialEdges` | `WorkflowBuilderEdge[]` | `[]` | Initial diagram edges (used by the `props` integration strategy). |
| `children` | `ReactNode` | `<DefaultLayout />` | Custom layout. Omit children for the default floating-overlay layout. |
| Prop | Type | Default | Description |
| ------------------- | -------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------------------------------- |
| `integration` | [`WorkflowBuilderIntegration`](#integration-strategies) | `{ strategy: 'localStorage' }` | How the builder loads and persists diagram data. |
| `nodeTypes` | [`PaletteItemOrGroup[]`](#node-types) | `[]` | Node type definitions rendered in the palette and used for validation. |
| `diagramTemplates` | `TemplateModel[]` | `[]` | Diagram templates available in the template selector. |
| `jsonForm` | [`WorkflowBuilderJsonFormConfig`](/guides/custom-jsonforms-control/) | — | Custom JSONForms renderers, cells, and translations. |
| `plugins` | [`WorkflowBuilderPlugin[]`](/guides/build-a-plugin/) | — | Plugin initializer functions. Each called once on first mount. |
| `name` | `string` | — | Workflow name displayed in the header. |
| `layoutDirection` | `'DOWN' \| 'RIGHT'` | `'DOWN'` | Flow direction of the diagram. |
| `initialNodes` | `WorkflowBuilderNode[]` | `[]` | Initial diagram nodes (used by the `props` integration strategy). |
| `initialEdges` | `WorkflowBuilderEdge[]` | `[]` | Initial diagram edges (used by the `props` integration strategy). |
| `isValidConnection` | [`WorkflowBuilderIsValidConnection`](#connection-validation) | — | Validate connections as the user draws them. |
| `reactFlowProps` | [`WorkflowBuilderReactFlowProps`](#advanced-reactflow-props) | — | Escape hatch for the underlying ReactFlow canvas. |
| `children` | `ReactNode` | `<DefaultLayout />` | Custom layout. Omit children for the default floating-overlay layout. |

## Compound subcomponents

Expand Down Expand Up @@ -167,6 +169,39 @@ type DidSaveStatus = 'success' | 'error' | 'alreadyStarted';

Today the runtime treats every non-empty resolution of `onDataSave` as "the save finished", surfacing the success-style snackbar — `'success'`, `'error'`, and `'alreadyStarted'` all behave the same way at the UI level. Throw from `onDataSave` instead of resolving to `'error'` if you need an error snackbar.

## Connection validation

`isValidConnection` decides whether a dragged connection is allowed. Return `false` to reject it: no edge is created, no flicker. It receives the resolved `sourceNode` / `targetNode` (plus the raw `connection`), so a rule can branch on node `data` without reaching into the store. Declare it at module scope (or memoize) to keep the reference stable.

```tsx
import { WorkflowBuilder, type WorkflowBuilderIsValidConnection } from '@workflowbuilder/sdk';

const isValidConnection: WorkflowBuilderIsValidConnection = ({ sourceNode, targetNode }) =>
!(sourceNode.data.type === 'start' && targetNode.data.type === 'start');

<WorkflowBuilder.Root isValidConnection={isValidConnection} />;
```

Validates interactive drags only, not programmatic edge writes (templates, paste, `setStoreEdges`). Fail-open: if an endpoint can't be resolved to a node, the connection is allowed.

## Advanced: ReactFlow props

`reactFlowProps` forwards extra props to the underlying ReactFlow canvas for things the SDK doesn't expose directly (zoom limits, key codes, `onNodeClick`, performance flags, …).

```tsx
import { WorkflowBuilder, type WorkflowBuilderReactFlowProps } from '@workflowbuilder/sdk';

const reactFlowProps = {
maxZoom: 1.5,
zoomOnDoubleClick: false,
onNodeClick: (_, node) => console.log(node.id),
} satisfies WorkflowBuilderReactFlowProps;

<WorkflowBuilder.Root reactFlowProps={reactFlowProps} />;
```

Props the SDK owns (graph data, the connection / selection / change handlers, type maps, `colorMode`, …) can't be set here. To observe SDK events use the listener APIs (`addNodeChangedListener`, …); to theme use the design tokens. Treat `reactFlowProps` as static config: runtime value changes may not apply immediately.

## Minimal example

```tsx
Expand Down
62 changes: 49 additions & 13 deletions packages/sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,21 @@ If you omit `<WorkflowBuilder.TopBar />`, use [`useWorkflowBuilderActions()`](ht

## `<WorkflowBuilder.Root>` props

| Prop | Type | Description |
| ------------------ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `nodeTypes` | `PaletteItemOrGroup[]` | Node type definitions. Appear in the palette and drive validation. **Must be a stable reference** — declare at module scope or memoize. |
| `nodeTemplates` | `WorkflowBuilderNodeTemplates` | Per-node-type custom renderers. Map of `data.type` → React component, overriding the default node renderer for that type. **Stable reference required** (same as `nodeTypes`). |
| `edgeTemplates` | `WorkflowBuilderEdgeTemplates` | Per-edge-type custom renderers. Map of `edge.type` → React component taking ReactFlow `EdgeProps`, overriding the built-in `labelEdge`. Unregistered types fall back to the default edge. **Stable reference required** (same as `nodeTypes`). |
| `diagramTemplates` | `TemplateModel[]` | Diagram templates available in the template selector. **Stable reference required** (same as `nodeTypes`). |
| `plugins` | `WorkflowBuilderPlugin[]` | Functions registering decorators. Synchronous, executed once. |
| `jsonForm` | `WorkflowBuilderJsonFormConfig` | Custom JsonForms renderers, cells, translations. |
| `integration` | `WorkflowBuilderIntegration` | Data source / sink. Defaults to `localStorage`. |
| `name` | `string` | Workflow name shown in the header. |
| `layoutDirection` | `'DOWN' \| 'RIGHT'` | Initial flow direction. |
| `initialNodes` | `WorkflowBuilderNode[]` | Starting diagram nodes. |
| `initialEdges` | `WorkflowBuilderEdge[]` | Starting diagram edges. |
| Prop | Type | Description |
| ------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `nodeTypes` | `PaletteItemOrGroup[]` | Node type definitions. Appear in the palette and drive validation. **Must be a stable reference** — declare at module scope or memoize. |
| `nodeTemplates` | `WorkflowBuilderNodeTemplates` | Per-node-type custom renderers. Map of `data.type` → React component, overriding the default node renderer for that type. **Stable reference required** (same as `nodeTypes`). |
| `edgeTemplates` | `WorkflowBuilderEdgeTemplates` | Per-edge-type custom renderers. Map of `edge.type` → React component taking ReactFlow `EdgeProps`, overriding the built-in `labelEdge`. Unregistered types fall back to the default edge. **Stable reference required** (same as `nodeTypes`). |
| `diagramTemplates` | `TemplateModel[]` | Diagram templates available in the template selector. **Stable reference required** (same as `nodeTypes`). |
| `plugins` | `WorkflowBuilderPlugin[]` | Functions registering decorators. Synchronous, executed once. |
| `jsonForm` | `WorkflowBuilderJsonFormConfig` | Custom JsonForms renderers, cells, translations. |
| `integration` | `WorkflowBuilderIntegration` | Data source / sink. Defaults to `localStorage`. |
| `name` | `string` | Workflow name shown in the header. |
| `layoutDirection` | `'DOWN' \| 'RIGHT'` | Initial flow direction. |
| `initialNodes` | `WorkflowBuilderNode[]` | Starting diagram nodes. |
| `initialEdges` | `WorkflowBuilderEdge[]` | Starting diagram edges. |
| `isValidConnection` | `WorkflowBuilderIsValidConnection` | Validate connections as the user draws them. See [Connection validation](#connection-validation). **Stable reference required.** |
| `reactFlowProps` | `WorkflowBuilderReactFlowProps` | Advanced escape hatch for the underlying ReactFlow canvas. See [Advanced: ReactFlow props](#advanced-reactflow-props). Treat as static config (runtime value changes may not apply immediately). |

Full reference (every public type, hook, and helper): <https://www.workflowbuilder.io/docs/api/core/workflowbuilder/>.

Expand Down Expand Up @@ -155,6 +157,40 @@ Plugins are synchronous. If you need async work (config fetch, WASM load, featur

Full guide: [Build a plugin](https://www.workflowbuilder.io/docs/guides/build-a-plugin/).

## Connection validation

`isValidConnection` decides whether a dragged connection is allowed. Return `false` to reject it: no edge is created, no flicker. It receives the resolved `sourceNode` / `targetNode` (plus the raw `connection`), so you branch on node `data` without reaching into the store.

```tsx
import { WorkflowBuilder, type WorkflowBuilderIsValidConnection } from '@workflowbuilder/sdk';

// Module scope keeps the reference stable.
const isValidConnection: WorkflowBuilderIsValidConnection = ({ sourceNode, targetNode }) =>
!(sourceNode.data.type === 'start' && targetNode.data.type === 'start');

export function App() {
return <WorkflowBuilder.Root isValidConnection={isValidConnection} />;
}
```

Validates interactive drags only, not programmatic edge writes (templates, paste, `setStoreEdges`). Fail-open: if an endpoint can't be resolved to a node, the connection is allowed.

## Advanced: ReactFlow props

`reactFlowProps` forwards extra props to the underlying ReactFlow canvas for things the SDK doesn't expose directly (zoom limits, key codes, `onNodeClick`, performance flags, …).

```tsx
const reactFlowProps = {
maxZoom: 1.5,
zoomOnDoubleClick: false,
onNodeClick: (_, node) => console.log(node.id),
} satisfies WorkflowBuilderReactFlowProps;

<WorkflowBuilder.Root reactFlowProps={reactFlowProps} />;
```

Props the SDK owns (graph data, the connection / selection / change handlers, type maps, `colorMode`, …) can't be set here. To observe SDK events use the listener APIs (`addNodeChangedListener`, …); to theme use [Theming](#theming). Treat `reactFlowProps` as static config: runtime value changes may not apply immediately.

## Theming

The editor exposes a small set of CSS custom properties for top-level styling. Override them on `:root` or scope to your app shell:
Expand Down
Loading
Loading