hart-estate-widget
Version:
HART Estate widget
776 lines (687 loc) • 24.9 kB
Markdown
The package is designed to present 2D and 3D floor plans generated by the AI service [getfloorplan.com](https://getfloorplan.com).
- [Quick Start](
- [Example vite.config.ts](
- [Example index.html](
- [Example index.js](
- [Example index.sass](
- [Docs](
- [Parameters](
- [REST API Object](
- [Versioning](
- [Copyright and License](
As a result, you will receive a website that can display various floor plans.

- Download NodeJS 18+
- Create a new project
```shell
npm init
```
- Download the required packages
```shell
npm install -S hart-estate-widget@4.0.0
npm install -S -D vite
npm install -S -D sass-embedded
```
- Example structure of project
```
.
├── src
│ ├── index.html
│ ├── index.js
│ ├── index.sass
│ └── logo.png
├── package.json
└── package-lock.json
```
- Copy-paste sample assets from below.
- Run with `rm -rf dist && npx vite`
- Open browser at:
http://localhost:1234/?id=73b833c3-072a-4ac2-9f5d-7f7ac3d1fc9c
> The query `id` parameter accepts the UUID4 received from [getfloorplan.com](https://getfloorplan.com)
> You can use these UUID4 for test purposes:
> - Scandy style: `228ba1dd-64d3-4d33-bcd7-b4c670bed40e`
> - Boho style: `f9032373-bb2c-416e-b0ab-20b8fd24d482`
> - England style: `b21871a2-2d5b-4beb-817b-ed750eebab9a`
> - Neutral style: `e8553134-0457-488c-8d3e-611b0e2be4d4`
> - Modern style: `73b833c3-072a-4ac2-9f5d-7f7ac3d1fc9c`
Insert the example into a file `vite.config.ts`
```ts
import { defineConfig } from 'vite';
export default defineConfig({
root: 'src',
build: { outDir: '../dist' },
server: { port: 1234, open: true },
});
```
Insert the example into a file `src/index.html`
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
<title>HART Estate Widget</title>
<script type="module" src="./index.js"></script>
<link rel="icon" href="./logo.png">
</head>
<body>
<div id="widget"></div>
</body>
</html>
```
Insert the example into a file `src/index.js`
```js
import { Api } from 'hart-estate-widget/build/api.js';
import { usePreloader } from 'hart-estate-widget/build/usePreloader.js';
const searchParams = new URLSearchParams(document.location.search);
const planId = searchParams.get('id');
const crmPlanId = searchParams.get('crmPlanId');
const baseUrl = 'https://backend.estate.hart-digital.com';
const loadData = async () => {
const {loadCrmWidgetData, loadWidgetData} = new Api(baseUrl);
return crmPlanId ? loadCrmWidgetData(crmPlanId) : loadWidgetData(planId);
};
const loadCreateWidget = async () => (await import('hart-estate-widget/build/createWidget.js')).createWidget;
const loadFovPlugin = async () => (await import('hart-estate-widget/build/plugins/FovPlugin.js')).FovPlugin;
const loadLogoUrl = async () => (await import('./logo.png')).default;
const loadStyle = async () => (await import('./index.sass')).default;
const loadDocument = async () => new Promise((resolve) => (window.onload = resolve));
const queue = [
loadData(),
loadCreateWidget(),
loadFovPlugin(),
loadLogoUrl(),
loadStyle(),
loadDocument(),
];
const cleanupPreloader = usePreloader();
Promise.all(queue).then(([data, createWidget, FovPlugin, logoUrl]) => {
const options = {
baseUrl,
logoUrl,
logoLinkUrl: 'https://getfloorplan.com',
};
const plugins = [
new FovPlugin(2),
];
createWidget('#widget', data, options, plugins).then(cleanupPreloader);
});
```
Insert the example into a file `src/index.sass`
```sass
*, *:before, *:after
-webkit-font-smoothing: antialiased
-moz-osx-font-smoothing: grayscale
font-family: 'Proxima Nova', sans-serif
text-decoration: none
font-weight: 400
color:
outline: none
padding: 0
margin: 0
box-sizing: border-box
-webkit-box-sizing: border-box
html, body
width: 100%
height: 100%
width: 100%
height: 100%
overflow: hidden
```
Here you can see a list of accessible options and examples of usage. There are accessible values for each option below in the block "Types of Elements".
```json
{
// Widget API base URL
"baseUrl": "https://backend.estate.hart-digital.com",
// Widget locale can be one of
// "en" - English language
// "ru" - Russian language
// "de" - German language
// "es" - Spanish language
// "ja" - Japanese language
// "nl" - Dutch language
// "bg" - Bulgarian language
// "he" - Hebrew language
"locale": "en",
// Path/link to the logo
"logoUrl": "https://getfloorplan.com/img/logo.58f6cd11.svg",
// Link opened when logo is clicked
"logoLinkUrl": "https://getfloorplan.com",
// Widget color settings in CSS color formal like: "rgba(255, 255, 255, 0)", "#ABCDEF", etc...
// "main" - main color of buttons, elements
// "mainText" - text color for buttons, elements contrasting with the main color
"colors": {
"main": "#FFA900",
"mainText": "#413E3E"
},
// First opened tab
"primaryTab": "panorama",
// Available widget tabs array with order:
// "vr" - VR button
// "panorama" - Panorama 360 tab
// "rotation" - Isometric plan rotation tab
// "plan" - Original plan tab
// "carousel" - 2d tab with middle cuts, plan and topView
"tabs": ["plan", "rotation", "panorama"],
// Controls in bottom bar, can be:
// "ruler" - Ruler enable/disable button (panorama tab only)
// "scale" - Scale x1, x2, x0.5 button (panorama tab only)
// "autoRotate" - Auto rotate enable/disable button (panorama tab only)
// "minusScale" - Scale minus button (panorama tab only)
// "plusScale" - Scale plus button (panorama tab only)
// "furnished" - Furnished / bareshell button (panorama tab only)
// "prevPanorama" - Prev panorama button (panorama tab only)
// "nextPanorama" - Next panorama button (panorama tab only)
// "2d" - 2D tab button
// "3d" - 3D tab button
// "360" - 360 tab button
// "isometry" - Isometry rotation arrows (rotation tab only)
// "floors" - Floors select with reverse order inside application frame
// "carousel" - carousel tab button with plan, top view, middle cuts
"controls": ["ruler", "scale", "autoRotate"],
// Enable/disable modal of instruction in 3D tour
"isInstructionsVisible": true,
// Enable/disable modal of instruction in ruler
"isRulerInstructionVisible": true,
// Show the ruler's instructions only once per session
"isRulerInstructionShownOnce": false,
// Enable/disable auto rotation in 3D tour
"isAutoRotate": false,
// Enable/disable device gyroscope for AR
"isGyroscopeEnabled": true,
// Show/hide fullscreen button
"isFullscreenButtonVisible": true,
// Show/hide current room type top label
"isRoomLabelVisible": false,
// Show next scale text or current
// true: next (current: 0.5, shows: 1x)
// false: current (current: 0.5, shows: 0.5x)
"isShowNextScaleText": false,
// Enable InteractiveRotationTab
// true: InteractiveRotationTab will show on interactive_top_view capability
// false: always use default RotationTab
"isInteractiveTabAvailable": true,
// Use multifloor map instead of default or topView
"isMultifloorMap": false,
// Automatic crop transparent part of images by rasterizer (may lag a bit)
"isNeedCropMultifloorMap": true,
// Precision of multifloor map crop
"multiFloorMapCropPrecision": 100,
// Move panorama control buttons to the application context
"isAbsolutePanoramaControls": false,
// Show prev/next room type labels on prev/next panorama buttons
"isShowLabelsOnPrevNext": true,
// Render links as div elements in UI layer
"isOverrideLinks": false,
// Enable room indexer button in rotation tab
"isRoomIndexerVisible": false,
// Enable room indexer button to rotate middle cuts
"isRoomIndexerRotate": false,
// Direction of rotation of rooms
"indexerButtonDirection": false,
// Show top view on rotation tab
"isShowTopViewOnRotationTab": true,
// Show left and right arrows in room scroll container
"isInteractiveScrollArrowsVisible": true,
// Show the "hide furniture" button with a separate switch
"isHiddenFurnitureSwitchVisible": true,
// Apply scale offset in 2D/3D
"isTransformScaleWithOffset": true,
// Apply transform offset in 2D/3D
"isTransformEnabled": true,
// Apply scale offset from screen center instead of cursor
"isTransformAlongScreenCenter": false,
// Resolve zero angle in middle cuts to rotate from top view more angle-seamlessly
"isNeedMiddleCutZeroAngle": false,
// Show VR button (on VR device connected)
"isVRVisible": true,
// Rotate the panorama using the left mouse button in any mode
"isPanoramaLeftMouseRotationOnly": false,
// Apply a fixed scale that is specified in the scales parameter
"isFixedScale": false,
// Inability to scale less than a given value (0.5x / 1x / 2x)
"minScaleLimit": "0.5x",
// Inability to scale more than a given value (0.5x / 1x / 2x)
"maxScaleLimit": "2x",
// "original" - use original_plan_img
// "miniplan" - use miniplan_img
"2DTabBehavior": "miniplan",
// Base camera persective FOV
"cameraFov": 75,
// Custom camera fovs by base FOV Scale
"cameraFovs": [
{ "text": "1x", "value": 1 },
{ "text": "2x", "value": 0.5 },
{ "text": "4x", "value": 0.25 },
{ "text": "0.5x", "value": 1.35 },
],
// Legacy camera fovs by scale texts (available: x1, x2, x05)
"scales": ["x1", "x2", "x05"],
// The size of the links on different scales
"linkScales": { "x1": 0.1, "x2": 0.1, "x05": 0.1 },
// Link pulse speed
"linkPulseSpeed": 0.75,
// Link pulse amplitude
"linkPulseAmplitude": 0.08,
// Link types include regex that should pulse
"linkPulseTypesIncludeRegex": ".*",
// Link types exclude regex that should pulse
"linkPulseTypesExcludeRegex": "door",
// Link icon include regex that should pulse
"linkPulseIconsIncludeRegex": "\\w+",
// Link icon exclude regex that should pulse
"linkPulseIconsExcludeRegex": "door",
// Use feet by default
"isFeetMetric": true,
// Panorama fade time in seconds
"panoramaFadeTime": 0.5,
// Panorama miniplan type, use "svg" or "original"
"panoramaMiniplanType": "svg",
// Overrides for interactive opacity
"interactiveHoverOpacity": 0.65,
"interactiveActiveOpacity": 0.8,
// Camera sensitivity
"cameraHorizontalSensitivity": 1,
"cameraVerticalSensitivity": 1,
"mobileCameraHorizontalSensitivity": 2,
"mobileCameraVerticalSensitivity": 2,
// Panorama tab config
"panoramaTabConfig": {
// Panorama link config
"linkConfig": {
// Base sprite size in meters
"baseSize": 0.1,
// Sprite scale on hover
"hoverScale": 1.5,
// Sprite scale change speed (0 = instant)
"scaleSpeed": 15,
// Overlay base width in pixels
"overlayWidth": 64,
// Overlay base height in pixels
"overlayHeight": 64
},
// Panorama config
"panoramaConfig": {
// Warp offset size
"warpSize": 0,
// Second panorama warp offset size
"secondaryWarpSize": 0,
// Normalize warp direction to 0..1
"isNormalizedWarp": true,
// Force camera rotation on every panorama
"forceCameraRotation": false
}
},
// Rotation tab config
"rotationTabConfig": {
// Index template, example: "{index}/{count}"
"indexTemplate": "{index}/{count}",
// Max area error delta to show area instead of size
"maxAreaErrorDelta": 0.15,
// Use comma decimal separator in meters
"isMetersUseComma": false,
// Meters area fixed precision, example: 1 -> 0.2 ; 2 -> 0.25 ...
"metersAreaPrecision": 1,
// Meters size fixed precision, example: 1 -> 0.2 ; 2 -> 0.25 ...
"metersSizePrecision": 1,
},
// Ruler info settings
"rulerInfoSettings": {
"fontSize": "20px",
"fontWeight": "Bold",
"lineHeight": "1",
"fontFamily": "Roboto Flex",
"textAlign": "center",
"borderRadius": 55,
"scale": 1.5,
"width": 256,
"height": 128,
"textFillColor": "#0008",
"textHoverColor": "#000f",
"fillColor": "#fff8",
"hoverColor": "#fffa",
"feetsPositionY": 100,
"feetsPrecision": 2,
"useFeetsComma": true,
"metersPositionY": 50,
"metersPrecision": 2
},
// MultiFloorMap settings
"multiFloorMap": {
// Available dimensions: px, vw, vh, vmax, vmin
// Subtractive margin suffix (applies to vw, vh, vmax and vmin): <dimension>m = <dimension> - m * 2 (margin)
// Subtractive: vwm, vhm, vmaxm and vminm
// Example: 512px, 37vh, 10vminm (vmin - 10 * 2), 10vmax
// Default multifloor map mode with 1-2 floors
"defaultMode": {
// Desktop size width/height
"desktopSize": {
"width": "37vh",
"height": "37vh",
},
// Extended desktop size width/height
"extendedDesktopSize": {
"width": "66vh",
"height": "66vh",
},
// Tablet size width/height
"tabletSize": {
"width": "372px",
"height": "372px",
},
// Mobile size width/height
"mobileSize": {
"width": "10vminm",
"height": "10vminm",
},
// MultiFloorMap padding
"padding": 10,
// MultiFloorMap gap
"gap": 4,
// MultiFloorMap maxHeight multiplier
"maxHeightMultiplier": 0.6,
// Calculate relative size between images
"isNeedRelativeSize": false
},
// Multifloor map mode with 3 or more floors
"manyMode": {
// ...
}
},
// Stairs settings
"stairs": {
// Forcing to search camera if upperCameraId or lowerCameraId was not presented
"forceCameraSearch": false
},
// Fallback config
"assetHostConfig": {
"version": 1,
"strategy": {
"order": ["origin", "group:same", "group:other"],
},
"failurePolicy": {
"http": [403, 404, 410, 429, 451, 500, 502, 503, 504],
"network": true,
"cors": true,
"contentMismatch": true,
},
"keyExtraction": {
"patterns": [
{ "re": "^https?://[^/]+/app_storage_production/public/(.*)$" },
{ "re": "^https?://[^/]+/storage/(.*)$" },
],
"fallback": "pathname-after-first-segment",
},
"routes": {
"cdn-origin.url": {
"template": "https://cdn-origin.url/{key}",
"probe": "range",
"cors": true,
},
"cdn2-origin.url": {
"template": "https://cdn-origin.url/{key}",
"probe": "head",
"cors": true,
},
// ...
},
"groups": {
"int": [
"cdn-origin.url",
// ...
],
"ru": [
"cdn2-origin.url",
// ...
],
},
},
// Lazy load config
"lazyLoad": {
// Is lazy load enabled
"enabled": true,
// Strategies (select only one of presented)
// Nearest cameras on the same floor
"strategy": {
"type": "nearest",
// Preload cameras count
"count": 3
},
// Preload all style on the same floor/camera
"strategy": {
"type": "style",
// Search for another camera with the same position if no same ID
"usePositionFallback": true
}
},
// Widget external integrations
"integrations": {
// https://docs.sentry.io/platforms/javascript/configuration/options/
"sentry": {
"dsn": "https://1234567890abcdef@sentry.com/12345"
}
},
// Query parameters for override, only on custom
"query": {
"primaryCameraPointId": "CameraPoint123456",
"primaryFloorNumber": 1,
"primaryVariantId": "<plan_id>",
"primaryStyleName": "scandy",
"primaryFov": 75,
"primaryTab": "panorama"
},
// Custom link icons
"linkIcons": {
"door": "https://site.com/door.png",
"doorHover": "https://site.com/door-hover.png",
"spot": "https://site.com/spot.png",
"spotHover": "https://site.com/spot-hover.png",
"stairUp": "https://site.com/stairUp.png",
"stairUpHover": "https://site.com/stairUp-hover.png",
"stairDown": "https://site.com/stairDown.png",
"stairDownHover": "https://site.com/stairDown-hover.png",
},
// Custom UI icons
"uiIcons": {
"tabs.2D": "https://site.com/2D.png",
"tabs.3D": "https://site.com/3D.png",
"tabs.360": "https://site.com/360.png",
"tabs.roomIndexer": "https://site.com/roomIndexer.png",
"room.kidroom": "https://site.com/kidroom.png",
"room.balcony": "https://site.com/balcony.png",
"room.gym": "https://site.com/gym.png",
"room.bathroom": "https://site.com/bathroom.png",
"room.bedroom": "https://site.com/bedroom.png",
"room.garage": "https://site.com/garage.png",
"room.hallway": "https://site.com/hallway.png",
"room.kitchen": "https://site.com/kitchen.png",
"room.office": "https://site.com/office.png",
"room.living": "https://site.com/living.png",
"room.storage": "https://site.com/storage.png",
"room.terrace": "https://site.com/terrace.png",
"room.toilet": "https://site.com/toilet.png",
"float.close": "https://site.com/close.png",
"float.extend": "https://site.com/extend.png",
"float.multiMap": "https://site.com/multiMap.png",
"float.shrink": "https://site.com/shrink.png",
"float.fullscreen": "https://site.com/fullscreen.png",
"instructions.move": "https://site.com/move.png",
"instructions.research": "https://site.com/research.png",
"instructions.createPoints": "https://site.com/createPoints.png",
"instructions.deletePoints": "https://site.com/deletePoints.png",
"controls.rulerOn": "https://site.com/rulerOn.png",
"controls.rulerOff": "https://site.com/rulerOff.png",
"controls.prevPanorama": "https://site.com/prevPanorama.png",
"controls.nextPanorama": "https://site.com/nextPanorama.png",
"controls.minusScale": "https://site.com/minusScale.png",
"controls.plusScale": "https://site.com/plusScale.png",
"controls.autoRotateOn": "https://site.com/autoRotateOn.png",
"controls.autoRotateOff": "https://site.com/autoRotateOff.png",
"controls.arrowLeft": "https://site.com/arrowLeft.png",
// Locale-related icon template: <locale>:<key>
"ru:tabs.2D": "https://site.ru/2d.svg",
"ru:tabs.3D": "https://site.ru/3d.svg"
},
// Overrides locale keys with custom text or translation
"localeOverrides": {
"rotate-plan": "Rotate plan",
"research-plan": "Research plan",
"create-points": "Create a point",
"delete-points": "Remove point",
"instructions-hint-text": "",
"ok": "Ok",
"ruler-ok": "Ok",
"furnished": "Furnished",
"bareshell": "Bareshell",
"feets": "Feets",
"meters": "Meters",
"metersAreaTemplate": "$0 m²",
"metersSizeTemplate": "$0 x $1 m",
"feetAreaTemplate": "$0",
"feetSizeTemplate": "$0 x $1",
"metersFormat": "$0 m",
"feetsFormat": "$0 ft",
"made-by-template": "$0 $1",
"made-by-prefix": "made by",
"made-by-link": "https://getfloorplan.com/",
"made-by-text": "getfloorplan.com",
"style": "Style",
"floor": "$0 floor",
"floorNumberEndings": {
"1": "$0st",
"2": "$0nd",
"3": "$0d",
"rest": "$0th"
},
"roomTypes": {
"KitchenLiving": "KitchLiv",
// ...
},
"fullRoomTypes": {
"KitchenLiving": "Kitchen Living",
// ...
},
"debugPanel": {
"header": "Debug Panel",
"minimizeMaximize": "Minimize/Maximize",
"planInformation": "Plan information",
"planID": "Plan ID",
"serviceID": "Service ID",
"totalArea": "Total Area (floor)",
"totalAreaTemplate": "$0 m²",
"floors": "Floors",
"rooms": "Rooms (floor / total)",
"qualityProcessing": "Quality & Processing",
"rtxQuality": "RTX Quality (panorama)",
"rtxQualityTopView": "RTX Quality (top view)",
"processingServer": "Processing Server",
"primaryVariant": "Primary Variant",
"variantID": "Variant ID",
"styleVariants": "Style Variants",
"navigation": "Navigation",
"unknown": "Unknown",
"qualityNames": {
"-1": "No RTX Preview",
"0": "Preview",
"1": "Low",
"2": "Medium",
"3": "High",
"4": "Ultra High",
"5": "Epic",
"6": "Unlit"
},
"valueNames": {
"classic": "Classic",
"proactive": "Proactive",
"portals": "Portals",
"mask": "Mask",
"true": "Enabled",
"false": "Disabled"
},
"capabilityNames": {
"camera_navigation": "Camera Navigation",
"floor_navigation": "Floor Navigation",
"has_panorama": "Has Panorama",
"hidden_furniture": "Hidden Furniture",
"interactive_top_view": "Interactive Top View",
"ruler": "Ruler",
"top_view_as_miniplan": "Top View Miniplan"
}
}
}
}
```
JSON object returned from backend
```js
export default {
primary_variant: "<id>", // primary variant to open
variants: [ // all plan variants
{
variant_info: {
style_name: "<name>", // the name of the variant style
is_renovation: true, // is there a renovation in the plan
},
plan_id: "<uuid>", // variant service plan uuid
json: "<url>", // json url for additional plan data
assets_path: "<path>", // plan assets base url
capabilities: { /*...*/ }, // plan capabilities
rendering_settings: { /*...*/ }, // plan rendering settings
primary_floor: 0, // primary plan floor index
floors: [ // plan floors
{
original_plan_img: "<url>", // absolute path to original plan image
miniplan_img: "<url>", // absolute path to miniplan image
top_view: {
image_template: "<template>", // template for top view decoding
room_ids_template: "<template>", // template for top view mask decoding
items: ["<filename>"], // top view image names
},
isometric_view: {
image_template: "<template>", // template for isometric images decoding
room_ids_template: "<template>", // template for isometric images masks decoding
items: ["<filename>"], // isometric images names
},
panorama_view: {
template: "<template>", // template for panorama image decoding
scene_depth_template: "<template>", // template for panorama depth image decoding
primary_camera_point_id: "<id>", // primary plan camera id
},
location: { X: 0, Y: 0, Z: 0 }, // floor location
height: 280, // floor height
vertices: [ /*...*/ ], // plan vertices data
rooms: [ /*...*/ ], // plan rooms data
camera_points: [ /*...*/ ], // plan camera points data
walls: [ /*...*/ ], // plan walls data
doors: [ /*...*/ ], // plan doors data
stairs: [ /*...*/ ], // plan stairs data
portals: [ /*...*/ ], // plan portals data
apertures: [ /*...*/ ], // plan apertures data
decors: [ /*...*/ ], // plan decors data
object_pairs: [], // color pairs for furniture data
room_pairs: [], // color pairs for room data
plan_meta: { /*...*/ }, // plan metadata
},
],
},
],
};
```
For the latest stable version refer to the latest up-to-date version in [Quick Start](
This project is maintained under the [Semantic Versioning guidelines](https://semver.org).
However, some versions are being developed for specific clients. **We do not recommend using them**, as changes in these versions are not documented and may affect your functionality.
The project code is licensed under the [GPLv3 license](https://www.gnu.org/licenses/gpl-3.0-standalone.html).
Project code and documentation copyright [hart-digital.com](https://hart-digital.com).
All renders of floor plans copyright [getfloorplan.com](https://getfloorplan.com).