hart-estate-widget 3.9.16 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,16 +3,16 @@
3
3
  The package is designed to present 2D and 3D floor plans generated by the AI service [getfloorplan.com](https://getfloorplan.com).
4
4
 
5
5
  # Table of Contents
6
- - [Quick Start](#quick-start)
7
- - - [Example index.html](#html)
8
- - - [Example index.tsx](#javascript)
9
- - - [Example index.sass](#sass)
10
- - [Docs](#docs)
11
- - - [Parameters](#parameters)
12
- - - [Types of Elements](#types-of-elements)
13
- - - [REST API Object](#widget-object-from-the-rest-api)
14
- - [Versioning](#versioning)
15
- - [Copyright and License](#copyright-and-license)
6
+ - [Quick Start](#quick-start)
7
+ - [Example vite.config.ts](#vite)
8
+ - [Example index.html](#html)
9
+ - [Example index.js](#javascript)
10
+ - [Example index.sass](#sass)
11
+ - [Docs](#docs)
12
+ - [Parameters](#parameters)
13
+ - [REST API Object](#widget-object-from-the-rest-api)
14
+ - [Versioning](#versioning)
15
+ - [Copyright and License](#copyright-and-license)
16
16
 
17
17
  # Quick Start
18
18
 
@@ -20,34 +20,32 @@ As a result, you will receive a website that can display various floor plans.
20
20
 
21
21
  ![node: ^18.0.0](https://img.shields.io/badge/nodeJS-^18.0.0-blue)
22
22
 
23
- - Download NodeJS 18+;
24
- - Create a new project;
25
- - Download the required packages
23
+ - Download NodeJS 18+
24
+ - Create a new project
25
+ ```shell
26
+ npm init
26
27
  ```
27
- npm install hart-estate-widget@2.7.5
28
- npm install sass@1.62.1
29
- npm install parcel@2.8.3
30
- npm install @parcel/transformer-sass@2.8.3
28
+ - Download the required packages
29
+ ```shell
30
+ npm install -S hart-estate-widget@4.0.0 # GFP Widget package
31
+ npm install -S -D vite # Vite bundler
32
+ npm install -S -D sass-embedded # Sass style compiler
31
33
  ```
32
- - Example structure of project:
34
+ - Example structure of project
33
35
  ```
34
36
  .
35
- ├── package-lock.json
36
- ├── package.json
37
37
  ├── src
38
- │ ├── assets
39
- ├── img
40
- │ │ ├── logo.png
41
- └── sass
42
- │ │ └── index.sass
43
- │ ├── index.html
44
- │ └── js
45
- └ └── index.tsx
38
+ │ ├── index.html
39
+ │ ├── index.js
40
+ │ ├── index.sass
41
+ │ └── logo.png
42
+ ├── package.json
43
+ └── package-lock.json
46
44
  ```
47
45
  - Copy-paste sample assets from below.
48
- - Run with `rm -rf dist && npx parcel ./src/index.html`
46
+ - Run with `rm -rf dist && npx vite`
49
47
  - Open browser at:
50
- http://localhost:1234/?id=228ba1dd-64d3-4d33-bcd7-b4c670bed40e
48
+ http://localhost:1234/?id=73b833c3-072a-4ac2-9f5d-7f7ac3d1fc9c
51
49
 
52
50
  > The query `id` parameter accepts the UUID4 received from [getfloorplan.com](https://getfloorplan.com)
53
51
  > You can use these UUID4 for test purposes:
@@ -57,68 +55,94 @@ http://localhost:1234/?id=228ba1dd-64d3-4d33-bcd7-b4c670bed40e
57
55
  > - Neutral style: `e8553134-0457-488c-8d3e-611b0e2be4d4`
58
56
  > - Modern style: `73b833c3-072a-4ac2-9f5d-7f7ac3d1fc9c`
59
57
 
58
+ ## VITE
59
+ Insert the example into a file `vite.config.ts`
60
+
61
+ ```ts
62
+ import { defineConfig } from 'vite';
63
+
64
+ export default defineConfig({
65
+ root: 'src',
66
+ build: { outDir: '../dist' },
67
+ server: { port: 1234, open: true },
68
+ });
69
+ ```
70
+
60
71
  ## HTML
61
72
  Insert the example into a file `src/index.html`
62
73
 
63
74
  ```html
75
+ <!DOCTYPE html>
64
76
  <html lang="en">
65
77
  <head>
66
78
  <meta charset="utf-8">
67
79
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
68
- <title>katmosfera</title>
69
- <link rel="icon" href="./assets/img/logo.png">
70
- <script src="./js/index.tsx" type="module"></script>
80
+ <title>HART Estate Widget</title>
81
+
82
+ <script type="module" src="./index.js"></script>
83
+ <link rel="icon" href="./logo.png">
71
84
  </head>
72
85
 
73
86
  <body>
74
- <section class="widget-section">
75
- <div class="widget" id="widget"></div> <!--must match the first attribute in 'new Widget('#widget', options);' -->
76
- </section>
87
+ <div id="widget"></div>
77
88
  </body>
78
89
  </html>
79
90
  ```
80
91
 
81
92
  ## JavaScript
82
- Insert the example into a file `src/js/index.tsx`
93
+ Insert the example into a file `src/index.js`
83
94
 
84
95
  ```js
85
- import { ApiStore, Widget, rotationModes } from 'hart-estate-widget';
86
- import '../assets/sass/index.sass'; // style
87
- import logo from '../assets/img/logo.png'; // logo
96
+ import { Api } from 'hart-estate-widget/build/api.js'
88
97
 
89
- const WIDGET_API_URL = 'https://backend.estate.hart-digital.com';
98
+ const searchParams = new URLSearchParams(document.location.search);
99
+ const planId = searchParams.get('id');
100
+ const crmPlanId = searchParams.get('crmPlanId');
90
101
 
91
- const createWidget = async (logoUrl) => {
92
- const widgetApiHandler = new ApiStore(WIDGET_API_URL)
102
+ const baseUrl = 'https://backend.estate.hart-digital.com';
93
103
 
94
- const { searchParams } = new URL(document.location);
95
- const planId = searchParams.get("id");
96
-
97
- const planData = await widgetApiHandler.loadWidgetData(planId);
98
- const options = { // the parameters you need that are described below in "Parameters"
99
- ...planData.parsed,
100
- api_url: WIDGET_API_URL,
101
- rotationMode: rotationModes.DEFAULT,
102
- tabs: ['panorama', 'rotation'],
103
- locale: 'en',
104
+ const loadData = async () => {
105
+ const {loadCrmWidgetData, loadWidgetData} = new Api(baseUrl);
106
+ return crmPlanId ? loadCrmWidgetData(crmPlanId) : loadWidgetData(planId);
107
+ };
108
+ const loadCreateWidget = async () => (await import('hart-estate-widget/build/createWidget.js')).createWidget;
109
+ const loadFovPlugin = async () => (await import('hart-estate-widget/build/plugins/FovPlugin.js')).FovPlugin;
110
+ const loadLogoUrl = async () => (await import('./logo.png')).default;
111
+ const loadStyle = async () => (await import('./index.sass')).default;
112
+ const loadDocument = async () => new Promise((resolve) => (window.onload = resolve));
113
+
114
+ const queue = [
115
+ loadData(),
116
+ loadCreateWidget(),
117
+ loadFovPlugin(),
118
+ loadLogoUrl(),
119
+ loadStyle(),
120
+ loadDocument(),
121
+ ];
122
+
123
+ Promise.all(queue).then(([data, createWidget, FovPlugin, logoUrl]) => {
124
+ const options = {
125
+ baseUrl,
104
126
  logoUrl,
105
- logo,
106
- }
127
+ logoLinkUrl: 'https://getfloorplan.com',
128
+ };
107
129
 
108
- new Widget('#widget', options); // must match the element id
109
- }
130
+ const plugins = [
131
+ new FovPlugin(2),
132
+ ];
110
133
 
111
- createWidget('https://yoursite/'); // create a widget
134
+ createWidget('#widget', data, options, plugins);
135
+ });
112
136
  ```
113
137
 
114
138
  ## SASS
115
- Insert the example into a file `src/sass/index.sass`
139
+ Insert the example into a file `src/index.sass`
116
140
 
117
141
  ```sass
118
142
  *, *:before, *:after
119
143
  -webkit-font-smoothing: antialiased
120
144
  -moz-osx-font-smoothing: grayscale
121
- font-family: 'Proxima Nova'
145
+ font-family: 'Proxima Nova', sans-serif
122
146
  text-decoration: none
123
147
  font-weight: 400
124
148
  color: #fff
@@ -128,325 +152,157 @@ Insert the example into a file `src/sass/index.sass`
128
152
  box-sizing: border-box
129
153
  -webkit-box-sizing: border-box
130
154
 
131
- body
155
+ html, body
132
156
  width: 100%
133
157
  height: 100%
134
- overflow: hidden
135
158
 
136
- .widget-section
159
+ #widget
137
160
  width: 100%
138
161
  height: 100%
139
- .widget
140
- width: 100%
141
- height: 100%
162
+ overflow: hidden
142
163
  ```
143
164
 
144
-
145
165
  # Docs
146
166
  ## Parameters:
147
167
  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".
148
168
 
149
169
  ```ts
150
- type TConfig = {
151
- /** Widget API Backend URL */
152
- api_url: 'https://backend.example.com/';
170
+ type TWidgetTab =
171
+ 'rotation' | // circular view images (order is mandatory)
172
+ 'plan' | // original plan image
173
+ 'panorama' ; // 360° images
174
+
175
+ type TPanoramaControlType =
176
+ 'ruler' | // Ruler button
177
+ 'scale' | // Scale 1x, 2x, 0.5x button
178
+ 'autoRotate' ; // Auto rotation button
179
+
180
+ type TAppColor =
181
+ 'main' | // Widget fill color
182
+ 'mainText' ; // Widget text color
183
+
184
+ type TWidgetLocale =
185
+ 'en' | // English language
186
+ 'ru' | // Russian language
187
+ 'de' | // German language
188
+ 'es' | // Spanish language
189
+ 'ja' ; // Japanese language
190
+
191
+ export type TConfig = {
192
+ /** Widget API base URL */
193
+ baseUrl: string;
194
+
195
+ /** Widget locale */
196
+ locale: TWidgetLocale;
197
+ /** Overrides locale keys with custom text or translation */
198
+ localeOverrides: Record<string, string>;
153
199
 
154
200
  /** Path/link to the logo */
155
- logo: '';
201
+ logoUrl: string;
156
202
  /** Link opened when logo is clicked */
157
- logoUrl: '';
158
-
159
- /** Widget container offsetWidth */
160
- width: 1920,
161
- /** Widget container offsetHeight */
162
- height: 1080,
163
- /** Automatically resize the widget to the size of the container when the window is resized */
164
- resizable: true;
165
-
166
- /** Widget localization language (default: en) */
167
- locale: 'en'
168
-
169
- /** Use door proactive rectangle instead of door icon. */
170
- enableDoorVisibility: false;
171
- /** Type of images that will present in rotation tab, used as whitelist filter, defaults ['carousel', 'plan', 'topView'] */
172
- availableRotationSteps: ['carousel', 'plan', 'topView'];
173
-
174
- /** Available widget tabs, defaults ['rotation', 'plan', 'panorama'] */
175
- tabs: ['rotation', 'plan', 'panorama'];
176
-
177
- /**
178
- * Mode of operation for plan images
179
- * - `rotationModes.DEFAULT` - top view mode (multiple perspectives) and stylized plan view
180
- * - `rotationModes.THREESIXTY` - image scrolling mode for circular view
181
- * */
182
- rotationMode: rotationModes.DEFAULT;
183
- /** Camera persective FOV */
184
- panoramaFov: 75;
185
- /** Enable/disable camera rotation between panoramas (if CameraPoint.Rotation.Yaw is defined in JSON) */
186
- enableCameraRotationBetweenPanoramas: true;
187
- /** Enable/disable transition for camera rotation between panoramas (if CameraPoint.Rotation.Yaw is defined in JSON and enableCameraRotationBetweenPanoramas enabled) */
188
- enableCameraTransitionBetweenPanoramas: false;
189
- /** Override primary camera point ID over JSON */
190
- primaryCameraPointId: 'CameraPointId12345678-12345678-12345678';
191
-
203
+ logoLinkUrl: string;
192
204
  /**
193
205
  * Widget color settings
194
206
  * - `main`: main color of buttons, elements
195
207
  * - `mainText`: text color for buttons, elements contrasting with the main color
196
208
  * */
197
- colors: {
198
- main: '#FFA900',
199
- mainText: '#413E3E',
200
- };
201
-
202
- /** Panorama icons */
203
- panoramaIcons: {
204
- /** Icon for camera point links in one room */
205
- spot: 'https://images.com/image-spot.svg';
206
- /** Icon for proactive camera point links in one room */
207
- interactiveSpot: 'https://images.com/image-interactive-spot.svg';
208
- /** Icon for door link */
209
- door: 'https://images.com/image-door.png';
210
- };
209
+ colors: Record<TAppColor, string>;
211
210
 
212
- /** Overrides locale keys with custom text or translation */
213
- dictionaryOverrides: {
214
- 'create-points': 'Create a point', // text for create point
215
- 'delete-points': 'Remove point', // text for delete point
216
- 'research-plan': 'Research plan', // text for research plan
217
- 'rotate-plan': 'Rotate plan', // text for rotate plan
218
- 'ok': 'Ok', // button text
219
- 'made-by-link': 'https://getfloorplan.com/', // watermark link
220
- 'made-by-text': 'getfloorplan.com', // watermark text
221
- 'instructions-hint-text': '', // additional text on the bottom of instruction modal
222
- 'floor': '$0 floor' // floor text
223
- };
211
+ /** First opened tab */
212
+ primaryTab: TWidgetTab;
213
+ /** Available widget tabs, defaults ['plan', 'rotation', 'panorama'] */
214
+ tabs: TWidgetTab[];
215
+ /** Path/link to the logo */
216
+ /** Controls can be: ruler, autoRotate or scale */
217
+ controls: TPanoramaControlType[];
224
218
 
225
219
  /** Enable/disable modal of instruction in 3D tour */
226
- instructionVisible: true;
220
+ isInstructionsVisible: boolean;
227
221
  /** Enable/disable auto rotation in 3D tour */
228
- autoRotate: false;
229
- /** Array of scale keys used as filter, can be x05, x1, ... */
230
- scales: ['x05', 'x1'];
222
+ isAutoRotate: boolean;
231
223
  /** Enable/disable device gyroscope for AR */
232
- enableGyroscope: true;
224
+ isGyroscopeEnabled: boolean;
225
+ /** Show/hide fullscreen button */
226
+ isFullscreenButtonVisible: boolean;
227
+ /** Show/hide current room type top label */
228
+ isRoomLabelVisible?: boolean;
233
229
 
234
- /**
235
- * Scale button type
236
- * - `img`: legacy button with icons x05, x1, etc...
237
- * - `text`: button with text content instead of icon x05, x1, etc...
238
- */
239
- scaleButtonType: 'img';
240
- /** Show floor number endings in floors locale translation */
241
- floorNumberEndingsVisible: false;
230
+ /** Camera persective FOV */
231
+ cameraFov: number;
232
+
233
+ /** Time for fade in (seconds) */
234
+ panoramaFadeTime: number;
242
235
 
243
- /**
244
- * Design type
245
- * - `standard`: bottom panel without sidebar, external floors select
246
- * - `sidebar`: all buttons in sidebar, also floors select in sidebar
247
- * - `custom`: use `bottombarContent` and `sidebarContent` to select required buttons manually
248
- */
249
- designType: 'sidebar';
250
- /** Bottom container button types to show */
251
- bottombarContent: [];
252
- /** Sidebar button types to show */
253
- sidebarContent: ['floors', 'ruler', 'scale', 'rotation', 'furniture'];
254
- /**
255
- * Buttons design version
256
- * - `two`: buttons design v2.0
257
- * - `three`: buttons design v3.0
258
- */
259
- tabsButtonsDesign: 'three';
260
- /** Proactive cursor type: 'pointer' or 'circle' */
261
- cursor: 'pointer';
262
- /** Enable/disable proactive cursor pulse */
263
- enableCursorPulse: true;
264
-
265
236
  /** Widget external integrations */
266
237
  integrations: {
267
- /** Configuration options for the Sentry Browser SDK. See: Sentry API BrowserOptions */
268
- sentry: BrowserOptions;
269
- };
270
-
271
- /** Allows movement only within a room or between rooms, can be interroom or intraroom */
272
- movementType: 'interroom';
273
-
274
- /** Options for DefaultLink graphics */
275
- default_link_options: {
276
- /** Use perspective size for DefaultLink sprite */
277
- use_real_size: false;
278
- /** Perspective size */
279
- real_size: 300;
280
- /** Perspective size scale on mobile devices */
281
- real_size_mobile_scale: 1;
282
- /** Minimal distance to prevent overscale in near distances */
283
- real_size_min_distance: 100;
284
- /** Minimal distance scale clamp on near distances */
285
- real_size_distance_min_scale: 0.1;
286
- /** Enable/disable scale on hover */
287
- hover_scale: 1.2;
288
- };
289
-
290
- /** Panorama fading options */
291
- fade_options: {
292
- /** Time for fade in (seconds) */
293
- fade_in_time: 0.5;
294
- /** Time for fade out (seconds) */
295
- fade_out_time: 0.5;
296
- };
297
-
298
- /** Options for DefaultLink graphics */
299
- ruler_options: {
300
- /** Font size for ruler text */
301
- font_size: 38;
302
- };
303
-
304
- /** Options for DefaultLink graphics */
305
- link_options: {
306
- /** Enable/disable link pulse on hover */
307
- enable_pulse: false;
308
- /** Pulse scale from */
309
- pulse_from_scale: 1;
310
- /** Pulse scale to */
311
- pulse_to_scale: 2;
312
- /** Pulse speed */
313
- pulse_speed: 1;
314
-
315
- /** Enable/disable scale on hover */
316
- enable_point_hover_scale: false;
317
- /** Hover scale */
318
- point_hover_scale: 1;
238
+ /** Configuration options for the Sentry Browser SDK. */
239
+ sentry?: BrowserOptions; // See https://docs.sentry.io/platforms/javascript/configuration/options/
319
240
  };
320
-
321
- /** Options for DefaultLink graphics */
322
- map_options: {
323
- /** Map type, should be 'default', 'top_view' or 'multi_floor' */
324
- type: 'default';
325
- /** Upscale top view resolution for better rendering */
326
- top_view_upscale: 2;
327
- /** Crop transparent bounds in multimap */
328
- crop_transparent?: true;
329
- };
330
-
331
- /** Options for Scene camera */
332
- camera_options: {
333
- /** Speed of fov changing per deltaTime */
334
- fov_change_speed?: 0;
335
- };
336
-
337
- /** Options for Portal link graphics */
338
- portal_options?: {
339
- /** Use overlay div elements instead of three.js graphics */
340
- use_overlay?: true;
341
- /** Generate portals from stairs */
342
- generate_from_stairs?: true;
343
- };
344
-
345
- /** Options for DefaultLink graphics */
346
- lazy_load_options: {
347
- /** Count panoramas to lazy load after move to another panorama */
348
- preload_count: 3;
349
- /** Force lazy load another non-loaded panoramas instead of skipping near cached panoramas */
350
- force_preload_non_cached: true;
351
- /** Preload depth maps */
352
- use_masks_preload: true;
353
- };
354
-
355
- /** Widget branding data */
356
- widget_branding: {
357
- /** Branded logo path */
358
- logo_path: 'https://es-logos.com/logo.png';
359
- /** Branded company name */
360
- company_name: 'ES Company';
361
- /** Branded company url */
362
- company_url: 'https://es.company.com';
363
- /** Branded widget language (See locale/*) */
364
- widget_language: 'es';
365
- }
366
-
367
- /** Show/hide fullscreen button */
368
- fullscreen_button_visible: false;
369
- /** Show/hide room type label */
370
- room_label_visible: false;
371
241
  };
372
242
  ```
373
243
 
374
- ## Types of elements
375
- ```js
376
- tabs: [
377
- 'rotation', // circular view images (order is mandatory)
378
- 'panorama', // 360° images
379
- ],
380
- rotationMode: [
381
- rotationModes.DEFAULT, // top view mode (multiple perspectives) and stylized plan view
382
- rotationModes.THREESIXTY, // image scrolling mode for circular view
383
- ],
384
- floors[0].rotate.type: [
385
- 'top_down', // full model
386
- 'middle_cut', // model with cut in the middle
387
- ],
388
- floors[0].panorama.type: [
389
- 'sphere', // 360° panorama
390
- 'cube', // panorama with 6 images (top, down, left, right, front, back)
391
- ],
392
- locale: [
393
- 'ru', // Russian language
394
- 'en', // English language
395
- 'es', // Spanish language
396
- 'de', // German language
397
- 'ja', // Japanese language
398
- ],
399
- scales: [
400
- 'x05',
401
- 'x075',
402
- 'x1',
403
- 'x125',
404
- 'x2',
405
- ],
406
- ```
407
-
408
244
  ## Widget object from the REST API
409
245
 
410
246
  JSON object returned from backend
411
247
 
412
248
  ```js
413
- name: "", // CRM plan ID (only for CRM API)
414
- original_plan_img: "", // deprecated, path to the original plan image
415
- original_plans_img: [ // array of paths to the original plan images
416
- ""
417
- ],
418
- styled_plan_img: "", // path to the styled plan image
419
- top_view_img: "", // path to the top view image
420
- json: { // JSON with data for 3D tour
421
- type: "furniture", // type of JSON (furniture, neural)
422
- value: "" // path to JSON
423
- },
424
- panorama: { // type of panorama and paths to 360° images
425
- type: "sphere", // 360° panorama or 'cube' for 6 images
426
- items: {
427
- [
428
- camera_id: "" // camera ID for panorama
429
- room_id: "" // room ID for panorama
430
- images: [ // array of paths to 360° sphere and sides of the cube if type is cube
431
- sphere: "",
432
- front: null,
433
- back: null,
434
- left: null,
435
- right: null,
436
- top: null,
437
- bottom: null,
438
- scene_depth: "", // path to EXR file for depth map if available
439
- object_ids: "", // path to EXR file for furniture object ids if available
440
- ]
441
- }
442
- ]
443
- },
444
- rotate: { // type of images and paths to circular view images
445
- type: "top_down", // full model or model with cut in the middle ('middle_cut')
446
- items: [ // array of paths to circular view images
447
- ""
448
- ]
449
- }
249
+ export default {
250
+ primary_variant: "<id>", // primary variant to open
251
+ variants: [ // all plan variants
252
+ {
253
+ variant_info: {
254
+ style_name: "<name>", // the name of the variant style
255
+ is_renovation: true, // is there a renovation in the plan
256
+ },
257
+ plan_id: "<uuid>", // variant service plan uuid
258
+ json: "<url>", // json url for additional plan data
259
+ assets_path: "<path>", // plan assets base url
260
+ capabilities: { /*...*/ }, // plan capabilities
261
+ rendering_settings: { /*...*/ }, // plan rendering settings
262
+
263
+ primary_floor: 0, // primary plan floor index
264
+ floors: [ // plan floors
265
+ {
266
+ original_plan_img: "<url>", // absolute path to original plan image
267
+ miniplan_img: "<url>", // absolute path to miniplan image
268
+
269
+ top_view: {
270
+ image_template: "<template>", // template for top view decoding
271
+ room_ids_template: "<template>", // template for top view mask decoding
272
+ items: ["<filename>"], // top view image names
273
+ },
274
+ isometric_view: {
275
+ image_template: "<template>", // template for isometric images decoding
276
+ room_ids_template: "<template>", // template for isometric images masks decoding
277
+ items: ["<filename>"], // isometric images names
278
+ },
279
+ panorama_view: {
280
+ template: "<template>", // template for panorama image decoding
281
+ scene_depth_template: "<template>", // template for panorama depth image decoding
282
+ primary_camera_point_id: "<id>", // primary plan camera id
283
+ },
284
+
285
+ location: { X: 0, Y: 0, Z: 0 }, // floor location
286
+ height: 280, // floor height
287
+
288
+ vertices: [ /*...*/ ], // plan vertices data
289
+ rooms: [ /*...*/ ], // plan rooms data
290
+ camera_points: [ /*...*/ ], // plan camera points data
291
+ walls: [ /*...*/ ], // plan walls data
292
+ doors: [ /*...*/ ], // plan doors data
293
+ stairs: [ /*...*/ ], // plan stairs data
294
+ portals: [ /*...*/ ], // plan portals data
295
+ apertures: [ /*...*/ ], // plan apertures data
296
+ decors: [ /*...*/ ], // plan decors data
297
+
298
+ object_pairs: [], // color pairs for furniture data
299
+ room_pairs: [], // color pairs for room data
300
+ plan_meta: { /*...*/ }, // plan metadata
301
+ },
302
+ ],
303
+ },
304
+ ],
305
+ };
450
306
  ```
451
307
 
452
308
  # Versioning
package/build/api.js ADDED
@@ -0,0 +1 @@
1
+ var r={d:(e,t)=>{for(var a in t)r.o(t,a)&&!r.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},o:(r,e)=>Object.prototype.hasOwnProperty.call(r,e)},e={};r.d(e,{j:()=>t});class t{baseUrl;constructor(r){this.baseUrl=r}get headers(){return{Accept:"application/json;widget-version=4.0.0"}}loadCrmWidgetData=async r=>{if(!r)return this.makeError(404,"No crmPlanId!");try{const e=await this.fetch(`/api/crm/plans/${r}/v4/widget`);return e.ok?e.json():this.makeErrorFromResponse(e,"Failed to get CRM Plan widget data!")}catch(r){return this.makeError(520,r.message)}};loadWidgetData=async r=>{if(!r)return this.makeError(404,"No planId!");try{const e=await this.fetch(`/api/plans/${r}/v4/widget`);return e.ok?e.json():this.makeErrorFromResponse(e,"Failed to get Plan widget data!")}catch(r){return this.makeError(520,r.message)}};loadJson=async r=>{try{return(await fetch(r)).json()}catch(r){return this.makeError(500,r.message)}};makeError(r,e="Unknown error"){return{error:{status:r,message:e}}}async makeErrorFromResponse(r,e){if("application/json"===r.headers.get("Content-Type")){const e=await r.json();if("message"in e&&"string"==typeof e.message)return this.makeError(r.status,e.message)}return this.makeError(r.status,e)}fetch(r,e={headers:this.headers}){return fetch(`${this.baseUrl}${r}`,e)}}var a=e.j;export{a as Api};