UNPKG

hart-estate-widget

Version:

HART Estate widget

776 lines (687 loc) 24.9 kB
# HART Estate Widget The package is designed to present 2D and 3D floor plans generated by the AI service [getfloorplan.com](https://getfloorplan.com). # Table of Contents - [Quick Start](#quick-start) - [Example vite.config.ts](#vite) - [Example index.html](#html) - [Example index.js](#javascript) - [Example index.sass](#sass) - [Docs](#docs) - [Parameters](#parameters) - [REST API Object](#widget-object-from-the-rest-api) - [Versioning](#versioning) - [Copyright and License](#copyright-and-license) # Quick Start As a result, you will receive a website that can display various floor plans. ![node: ^18.0.0](https://img.shields.io/badge/nodeJS-^18.0.0-blue) - Download NodeJS 18+ - Create a new project ```shell npm init ``` - Download the required packages ```shell npm install -S hart-estate-widget@4.0.0 # GFP Widget package npm install -S -D vite # Vite bundler npm install -S -D sass-embedded # Sass style compiler ``` - 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` ## VITE 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 }, }); ``` ## HTML 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> ``` ## JavaScript 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); }); ``` ## SASS 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: #fff outline: none padding: 0 margin: 0 box-sizing: border-box -webkit-box-sizing: border-box html, body width: 100% height: 100% #widget width: 100% height: 100% overflow: hidden ``` # Docs ## Parameters: 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" } } } } ``` ## Widget object from the REST API 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 }, ], }, ], }; ``` # Versioning For the latest stable version refer to the latest up-to-date version in [Quick Start](#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. # Copyright and license 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).