Archived document
You are looking at an archived document, the latest version can be found here.
Introduction
The editor app is the app where people can create their floor plans and interior designs. The scope of the editor is limited to one floor plan project. Think of a project as a house for a family with the possibility of multiple floor levels, bigger than a room and smaller than an apartment building. You can see a demo of the editor on the Floorplanner website.
This page explains how you can embed the editor app into your own HTML page.
Floorplanner Enterprise subscription
Before you start, make sure you have a Floorplanner Enterprise subscription and an API key. You need an API key to be able to log in as a certain user so you can load and save his/her floor plan projects. Have a look at the Floorplanner pricing page for more info about the Enterprise subscription.
Setup HTML & CSS
Let’s start with a minimal HTML page to embed the editor app. There are some placeholders present within square brackets, e.g. [AUTH_TOKEN]
, these must be replaced with actual values. You can scroll down to find more info about these placeholders. The scripts will take care or loading the latest version of the editor, including its stylesheets.
The editor app nests itself inside a target HTML node. In the HTML example below, this is done using a
id="fp-editor-container"
. In this sample the editor will take up 1024px width and 880px height
of the target div (#fp-editor-container). The height must be set explicitly, using min-height
is not sufficient since a height: 100% within such element is not respected. The dimensions used in the example are the recommended minimum width and height attributes, you can go below this but in some cases, the editor needs at least this minimum amount of space.
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<meta content="initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
</head>
<body>
<div id="fp-editor-container" style="width: 1024px; height: 880px;"></div>
<script data-floorplanner-editor src="https://fp-editor-cdn.floorplanner.com/embed.js" crossorigin="anonymous"></script>
<script type="text/javascript">
initFPEditor({
projectId: [PROJECT_ID],
mountSelector: '#fp-editor-container',
user: {
id: "[USER_ID]",
auth_token: "[AUTH_TOKEN]",
permissions: ['save'],
},
language: 'en-US',
}).then(api => (window.api = api));
</script>
</body>
</html>
Placeholders
Within the HTML, the placeholders [USER_ID]
, [AUTH_TOKEN]
and [PROJECT_ID]
must be replaced with real values. Below this section, you can find out how to retrieve each individual component.
Replacing AUTH_TOKEN and USER_ID
User ID and Authentication tokens can be retrieved by making an API request with your API key (scroll to bottom to find it). The following is an example cURL request. Do not forget to replace [YOUR_API_KEY]
with your actual API key:
curl --user [YOUR_API_KEY]:x https://floorplanner.com/api/v2/users/token.json
This will return json in the following format:
{
"token" : "eyJ0eXAiOiJKV1QiLQL1bGciOiJIUzI1NiJ9.eyJhY3Rpb24abaJsb2dpbiIsIm1vZGVsIjoiVXNlciIsImlkIjoxMDMsImlhdCI6MTUyMTczMTIwMH0.juutj2LGAVDUAV103Cz_viPvIfcPcs6weIPl3eFtsdM",
"user_id" : 1337
}
Within the HTML template, replace [AUTH_TOKEN]
with the value of the token
key inside the result JSON. Also replace [USER_ID]
with the value of user_id
inside the result JSON.
Replacing project_id
See the following API call to fetch all the projects you can access, their ID will be part of the JSON output: Projects#index. Replace the [PROJECT_ID]
in the HTML template with any of these ID’s.
IMPORTANT: [PROJECT_ID]
must be of type number, thus 123 and not "123".
Editor settings
The settings object can be used to customize certain parts of the editor, see the sample below:
var settings = {
// Set the domain used for requests from the editor.
// You can remove this setting when using the production environment.
apiDomain: 'sandbox.floorplanner.com',
// Toggles between metric and imperial measurement systems [true, false]
useMetric: true,
// A template offers a way to add consistent branding to floorplans and interior renders.
// It's a collection of style and export settings which can be modified in the website.
templateId: 160,
// To use the branding styling and settings you will need to the set the brandingId property.
// This can be a ID or the subdomain name of the branding. If it's a subdomain, be sure
// to uses quotations around the value.
brandingId: 1,
// Type of editor with the kind property. These are the optional values:
// 'spaceplanner', 'editor', 'viewer' and 'roomplanner'
kind: 'editor',
// User info
user: {
// The Floorplanner user id
id: 1337,
// Optional, this will set the default email address for the 2D & 3D exports
email: '[email protected]',
// An array of permissions:
// 'save' shows the save button in the top bar
// 'no-export' hides the 2D & 3D export buttons in the top bar
// 'hide-email' hides the email field at the bottom of the export popup
// 'no-back-to-dashboard' hides the Back to dashboard button in the sidebar
// 'ntl' turns off the cooldown period for renders
permissions: ['save', 'no-export'],
// Authentication token to log in as a certain user
auth_token: 'eyJ0eXAiOiJKV1QiLQL1bGciOiJIUzI1NiJ9.eyJhY3Rpb24abaJsb2dpbiIsIm1vZGVsIjoiVXNlciIsImlkIjoxMDMsImlhdCI6MTUyMTczMTIwMH0.juutj2LGAVDUAV103Cz_viPvIfcPcs6weIPl3eFtsdM'
}
};
Unmounting the editor with React
If you are embedding the editor in a single-page webapp, you need to make sure you unmount the editor before you initialize it again.
ReactDOM.unmountComponentAtNode(document.getElementById('fp-editor-container'));
This will ensure that references to the editor are released and there will be just one instance after you initialize it again.
API methods
It possible to trigger certain actions inside the editor by calling methods on the editor API object. For example, this will trigger a save action:
fpEditor.save();
Methods and properties available on the editor API object are:
Method/Property | Description |
---|---|
save() | Saves the current floor plan. |
isDirty | Returns true if there are unsaved changes. |
state | Returns the state of the Floorplan object (walls, items, areas..) displayed in the editor. |
meta | Returns metadata about areas and outer walls of the Floorplan object. |
zoomIn() | Zooms in. |
zoomOut() | Zooms out. |
zoomAll() | Zooms to fit the view. |
dropItem(id, pos) | Drops an item onto the plan. id is 40-character item id, pos is {x,y} where x and y are viewport coordinates. You can use MouseEvent.clientX and clientY there. |
view | Write-only. Should be either ‘2D’ or ‘3D’. Switches to the corresponding view. |
designId | Write-only. Given an id of the design of current project, loads that design into the view. |
project | Read-only. Data about project, including floors and designs. Meant to be used to list project’s floors and designs and to switch betwen them (setting the /designId/ property). |
You can also subscribe to following events:
Property | Description |
---|---|
onSave | The function assigned to this property will be called whenever the plan was saved. |
onUpdated | Will be called whenever internal state of the floorplan is updated. NB! The first update happens when the design is loaded. |
onQuit | Will be called whenever the editor is going to quit. NB! This handler replaces the default one, which sets window.location to ‘/dashboard’. So if your goal is to navigate the user to some other page, you have to do it in the body of the handler. |
onExport | Will be called whenever an export was requested. The assigned function will get a single input parameter, either “interior” or “floorplan”, indicating the type of the export. |
Callback examples
How to assign an event handler:
fpEditor.onUpdated = (data) => console.log('floorplan updated', data);
Get list of products of unique products with count on each update:
fpEditor.onUpdated = (data) => {
// Get all products from state and map the asset data
var allProducts = data.state.items.map((item) => item.asset);
// Get unique products with counts
var uniqueProducts = [];
for (i = 0; i < allProducts.length; i++) {
var index = uniqueProducts.findIndex(
(product) => product.id === allProducts[i].id
);
if (index >= 0) {
uniqueProducts[index].count++;
} else {
uniqueProducts.push({...allProducts[i], count: 1});
}
}
console.log('unique products', uniqueProducts);
};
Get product added and product removed:
var products = fpEditor.state.items;
fpEditor.onUpdated = (data) => {
// Get changed item
if (products.length < data.state.items.length) {
console.log('product added', data.state.items[data.state.items.length - 1]);
} else if (products.length > data.state.items.length) {
var allProducts = products.map((product) => product.asset.id);
var currentProducts = data.state.items.map((product) => product.asset.id);
var removedProductIds = allProducts.filter((d) => !currentProducts.includes(d));
var removedProducts = products.filter(
(product) => removedProductIds.includes(product.asset.id)
);
console.log('product(s) removed', removedProducts);
}
// Update products array
products = data.state.items;
};