> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ploterr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add a plan editor

> Give your users a 2D plan that they edit with the pointer.

export const LiveExample = ({path, title = 'Live example', height = 460}) => {
  return <Frame>
      <iframe src={`https://floorkit-examples.ploterr.com/floorkit/${path}?embed=1`} ref={frame => {
    if (!frame || !['localhost', '127.0.0.1'].includes(window.location.hostname)) return;
    const local = `http://localhost:5555/${path}?embed=1`;
    if (frame.src !== local) frame.src = local;
  }} title={title} loading="lazy" allow="fullscreen" style={{
    width: '100%',
    height: `${height}px`,
    border: 0,
    display: 'block'
  }} />
    </Frame>;
};

The plan editor shows one plan view of your project. The user selects, drags and draws in it. Each action becomes one step in the history of the project manager, so undo, redo and the 3D view work with no more code.

<LiveExample path="guides/add-a-plan-editor" />

## Before you start

Your project has a plan view with the id `ground-plan`. See [Create a plan view](/floorkit/guides/create-a-plan-view).

## Mount the editor

Open the project in a `ProjectManager`. The manager builds the building and gives each editor the same session.

```typescript theme={null}
import { ProjectManager, type ManagedPlanEditor, type ProjectFile } from '@opengeometry/floorkit';

export function openPlanProject(project: ProjectFile): ProjectManager {
  return new ProjectManager(project, {
    onError: (error) => console.error('Floorkit rejected the edit', error),
  });
}
```

Then give the manager an element that has a size, and the id of the view.

```typescript theme={null}
export function mountPlanEditor(
  manager: ProjectManager,
  container: HTMLElement,
): Promise<ManagedPlanEditor> {
  return manager.mountPlanEditor(container, 'ground-plan');
}
```

## Edit a wall

When the pointer is on an element, the editor shows it in light blue. When the user selects a wall, the editor shows a blue outline around the wall, a line along the wall and these handles:

| The user drags | Result |
| - | - |
| A dot at an end | Only that end moves. The other wall at the corner stays where it is. |
| The dot at the middle | The wall moves sideways. The walls at its ends become longer or shorter. |
| The wall itself | All segments of the wall move, with their doors and windows. |

An end that leaves a corner is free. An end released near the end of another wall joins that end, and the two walls make a corner. An end released near the body of another wall joins that wall. The green square shows that the end will join. When an end leaves a corner inside one saved wall, Floorkit cuts the saved wall there and gives the new part its own id.

The walls, their doors and their windows move while the user drags. When the user releases the pointer, Floorkit saves one edit command. Thus one drag is one step for undo.

## Edit a door or a window

When the user selects a door or a window, the editor shows one dot on it.

| The user drags the dot | Result |
| - | - |
| Along its wall | The door moves along the wall. |
| Near a different wall | The door moves into that wall. |
| More than 0.35 m from every wall | The door leaves its wall and becomes a loose door. |
| On a loose door | The loose door moves. It does not go into a wall. |

The door moves while the user drags, and the cut in the wall moves when the user releases the pointer. One drag is one edit command.

## Edit a slab, a roof, a room or a stair

When the user selects one of these, the editor shows its handles.

| Element | Handles |
| - | - |
| Slab | A dot at each corner moves that corner. A dot at the middle of an edge adds a corner. |
| Roof | A dot at each corner moves that corner. |
| Room | A dot at each corner moves that corner. |
| Stair | A dot at the start point moves the stair. Three smaller dots set its width, the depth of its treads and the depth of its landing. |

The 3D editor also has a handle for the height: the thickness of a slab, the eave of a roof, the height of a room and the base of a stair.

The element changes while the user drags. One drag is one `reshape` command.

## Edit a mass

A plan shows the masses of the site, the parcel and the dashed setback line of its zoning rules when its view has `showMassing: true`. The parcel and the setbacks are drawn only: a click passes through them, and a `replace-site` command draws them again. The switch is off by default. Turn it on in the saved view, or with a command, which is one undo step:

```typescript theme={null}
export function showMassing(manager: ProjectManager): void {
  const view = manager.snapshot().views.find((item) => item.id === 'ground-plan');
  if (view?.kind !== 'plan') return;
  manager.apply({
    kind: 'replace-project-item',
    list: 'views',
    id: view.id,
    item: { ...view, showMassing: true },
  });
}
```

The plan then draws the outline of each part of the mass of every building, under the storey. A mass works like a slab:

| The user | What happens |
| - | - |
| Clicks a part | That part (the tower or the podium) is the one the handles edit. |
| Drags a dot at a corner | That corner of the footprint of the part moves. |
| Drags a dot at the middle of an edge | A corner is added. |
| Drags the part | The part moves. The other parts stay. |
| Drags the round grip | The part turns about the centre of its footprint. |
| Presses Delete | The part goes. If it is the last part, the mass goes. |

Each drag and each Delete is one `replace-mass` command, as in the 3D editor. To show live numbers, such as the floor area, while the user drags, set `editor.renderer.onDrag`; it is called on each frame of a drag, before anything is saved. The plan has no handle for the height: change it in the 3D editor or with a command. The manager refuses a gesture that would remove a storey that a plan view shows, and Delete keeps such a storey as a plain storey.

## Let the user draw a wall

`startWallDrawing` starts the wall tool. The user clicks each point. The Enter key finishes the wall and the Escape key cancels it.

```typescript theme={null}
export function startWallTool(editor: ManagedPlanEditor): void {
  editor.startWallDrawing({ id: `wall-${Date.now()}`, thickness: 0.2 });
}
```

## Set how the user pans and zooms

A two-finger scroll pans the plan and a pinch zooms it. Ctrl or Cmd with the wheel also zooms. For a user with a mouse, make the wheel zoom.

```typescript theme={null}
import { MOUSE_NAVIGATION } from '@opengeometry/floorkit';

export function useMouseNavigation(editor: ManagedPlanEditor): void {
  editor.renderer?.setNavigation(MOUSE_NAVIGATION);
}
```

`setNavigation` also takes your own settings. See [`createModelRenderer`](/floorkit/api/views/create-model-renderer#notes).

## Add undo, redo and save

The manager has one history for the building, the views and the sheets. The undo key in the editor uses the same history.

```typescript theme={null}
export function planActions(manager: ProjectManager) {
  return {
    undo: () => manager.undo(),
    redo: () => manager.redo(),
    save: () => manager.save(),
  };
}
```

`undo()` and `redo()` return `null` when there is no step. `save()` returns the project text with the current building. Store this text. See [Save and reopen](/floorkit/guides/save-and-reopen).

## Remove the editor

`dispose()` of the manager removes each plan editor and then the session.

```typescript theme={null}
export function closePlanEditor(manager: ProjectManager): void {
  manager.dispose();
}
```

To remove one plan editor and keep the project open, call `dispose()` of that editor.

## Rules

* You can mount more than one plan editor on one manager, for example one for each storey.
* A door or a window leaves its wall when the user drags it more than 0.35 m from every wall. The option `hostedOpeningDetachTolerance` of the manager sets that distance in metres.
* While a plan editor shows a view, you cannot remove that view or make it a view that is not a plan. The manager throws `ProjectFileError` with the code `view-in-use`.
* If the editor shows a view that you added in this session, an undo that reaches the "add view" step fails with the same code. Dispose that editor first.
* After a change of the views, the sheets or the markers, the manager draws each mounted plan again.
* A mass is drawn under the elements of the storey. Where a slab or a room covers it, a click selects the slab or the room.

## Without the manager

If your app makes the `BuildingSession` itself, make the editor with `ManagedPlanEditor`. See the [ManagedPlanEditor API](/floorkit/api/editor/managed-plan-editor).

## Next

<CardGroup cols={2}>
  <Card title="Add a 3D editor" icon="cube" href="/floorkit/guides/add-a-3d-editor">
    Edit the same building in 3D.
  </Card>

  <Card title="ProjectManager API" icon="code" href="/floorkit/api/editor/project-manager">
    See each member of the manager and the project commands.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.