feat: add RoomIllustrationReact component and update TypeScript configuration

- Introduced a new React component  in  that wraps the  element.
- Updated TypeScript configuration to change module resolution from \node\ to \bundler\ and included a new JavaScript file for type checking.
- Updated  to add new dependencies including , , and others for improved functionality and compatibility.
This commit is contained in:
2026-04-22 16:49:03 +05:45
parent 39ead94535
commit daaa5bd7ad
14 changed files with 3474 additions and 27 deletions

View File

@@ -47,9 +47,15 @@
"react": "^18.0.0" "react": "^18.0.0"
}, },
"dependencies": { "dependencies": {
"@types/pako": "^2.0.4",
"@types/three": "^0.184.0",
"axios": "^1.15.2",
"javascript-obfuscator": "^5.4.1", "javascript-obfuscator": "^5.4.1",
"lit": "^3.2.0", "lit": "^3.2.0",
"rollup-plugin-obfuscator": "^1.1.0" "pako": "^2.1.0",
"pure-rand": "^8.4.0",
"rollup-plugin-obfuscator": "^1.1.0",
"three": "^0.184.0"
}, },
"devDependencies": { "devDependencies": {
"@11ty/eleventy": "^1.0.1", "@11ty/eleventy": "^1.0.1",

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,168 @@
import {LitElement, html, css} from 'lit'; // src/room-illustration.ts
import {customElement, property} from 'lit/decorators.js';
import {LitElement, html, css} from 'lit';
import {customElement, property, query, state} from 'lit/decorators.js';
import {RoomViewHelper} from './helpers/RoomViewHelper';
import type {
RoomData,
DesignDetails,
CarpetOptions,
AvailableSize,
} from './types';
let roomViewHelper = new RoomViewHelper();
@customElement('room-illustration')
export class RoomIllustration extends LitElement {
// ─── Props in (replaces all useXxxState() hook reads) ────────────────
@property({type: Object}) roomData: RoomData | null = null;
@property({type: Object}) designDetails: DesignDetails | null = null;
@property({type: Object}) carpetOptions: CarpetOptions | null = null;
@property({type: Object}) activeColor: {Color: string} | null = null;
@property({type: Object}) selectedSize: AvailableSize | null = null;
@property({type: Boolean}) isIdle = false;
// ─── Internal state (replaces useState) ──────────────────────────────
@state() private shotReady = false;
@state() private transition = -1;
// ─── Canvas refs (replaces useRef) ───────────────────────────────────
@query('.container') private containerEl!: HTMLDivElement;
@query('.bg-canvas') private bgCanvas!: HTMLCanvasElement;
@query('.three-canvas') private threeCanvas!: HTMLCanvasElement;
@query('.mask-canvas') private maskCanvas!: HTMLCanvasElement;
@query('.shadow-canvas') private shadowCanvas!: HTMLCanvasElement;
@query('.input-canvas') private inputCanvas!: HTMLCanvasElement;
@query('.obj-container') private objContainer!: HTMLDivElement;
// ─── Lifecycle ────────────────────────────────────────────────────────
override firstUpdated() {
// Equivalent to useMount in React — canvases are in the DOM
// roomViewHelper.initCanvas({
// bgCanvas: this.bgCanvas,
// threeCanvas: this.threeCanvas,
// maskCanvas: this.maskCanvas,
// shadowCanvas: this.shadowCanvas,
// container: this.containerEl,
// inputCanvas: this.inputCanvas,
// objCanvasContainer: this.objContainer,
// });
window.addEventListener('resize', () => this.sizeContainers());
}
override updated(changed: Map<string, unknown>) {
// if (changed.has('roomData') && this.roomData) {
// this.loadRoom();
// }
// if (changed.has('carpetOptions') && this.carpetOptions && this.shotReady) {
// this.updateCarpet();
// }
// if (changed.has('activeColor') && this.activeColor && this.shotReady) {
// roomViewHelper.updateBackground({
// dominantColorHex: this.activeColor.Color,
// });
// roomViewHelper.updateMask();
// roomViewHelper.updateShadow();
// }
// if (changed.has('selectedSize') && this.selectedSize) {
// roomViewHelper.change3dObjectScale(this.selectedSize);
// }
}
override disconnectedCallback() {
super.disconnectedCallback();
window.removeEventListener('resize', () => this.sizeContainers());
// roomViewHelper.clearAll();
}
// ─── Internal methods (replaces inline useEffect logic) ──────────────
private async loadRoom() {
// if (!this.roomData) return;
// const {config, baseUrl, files, shot} = this.roomData;
// roomViewHelper.clearAll();
// roomViewHelper.initConfig({baseUrl, config, files});
// this.sizeContainers();
// await this.initShot({shot: shot ?? config.defaultShot});
}
private async initShot(options: {shot: string}) {
// mirrors initShot logic from IllustrationView/index.jsx
// ...
this.shotReady = true;
this.emitRenderingComplete();
}
private async updateCarpet() {
if (!this.carpetOptions) return;
// await roomViewHelper.updateCarpetRotation(this.carpetOptions.rotation);
// await roomViewHelper.updateCarpetPosition(this.carpetOptions.position);
// await roomViewHelper.updateShadow();
}
private sizeContainers() {
// resize canvas to container size
}
// ─── Events out (replaces callback props) ────────────────────────────
private emitRenderingComplete() {
this.dispatchEvent(
new CustomEvent('rendering-complete', {bubbles: true, composed: true})
);
}
private emitSizesChange(sizes: AvailableSize[]) {
this.dispatchEvent(
new CustomEvent('sizes-change', {
detail: sizes,
bubbles: true,
composed: true,
})
);
}
private emitActiveClick() {
this.dispatchEvent(
new CustomEvent('active-click', {bubbles: true, composed: true})
);
}
// ─── Template (replaces JSX canvas layout) ───────────────────────────
@customElement('room-view')
export class RoomView extends LitElement {
static override styles = css` static override styles = css`
:host { :host {
display: block;
position: relative;
width: 100%;
height: 100%;
}
.container {
position: relative;
width: 100%;
height: 100%;
}
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
} }
`; `;
override render() { override render() {
return html`<h2>RoomView</h2>`; return html`
} <div class="container">
} <canvas class="bg-canvas"></canvas>
<canvas class="three-canvas"></canvas>
declare global { <canvas class="mask-canvas"></canvas>
interface HTMLElementTagNameMap { <canvas class="shadow-canvas"></canvas>
'room-view': RoomView; <canvas class="input-canvas"></canvas>
<div class="obj-container"></div>
</div>
`;
} }
} }

View File

@@ -1,8 +0,0 @@
import React from 'react';
import {createComponent} from '@lit/react';
import {RoomView} from './index';
export const RoomViewReact = createComponent({
tagName: 'room-view',
elementClass: RoomView,
react: React,
});

View File

@@ -0,0 +1,75 @@
// src/types.ts
import * as THREE from 'three';
export interface RoomData {
Name: string;
Files: string[];
Dir: string;
baseUrl: string;
config: RoomConfig;
shot?: string;
}
export interface RoomConfig {
version: number;
roomType: 'roomview' | 'illustration' | 'photographic' | 'perspective';
dims: {width: number; height: number};
name: string;
hasShadow: boolean;
mouseControls: boolean;
realTimeDynamicRendering: boolean;
roomElements: Record<string, unknown>;
roomAssets: Record<string, unknown>;
scene1: {surface1: {rotation: number[]; position: number[]}};
}
export interface DesignDetails {
hash: string;
fullpath: string;
designDetails: Record<string, unknown>;
updateDesignTiles?: boolean;
updateNormapTiles?: boolean;
}
export interface CarpetOptions {
rotation: [number, number, number];
position: [number, number, number];
}
export interface AvailableSize {
width: number;
height: number;
unit: string;
}
export interface MyRoomPointsOBJ {
floorpoints: {x: number; y: number}[];
notfloorpoints: {x: number; y: number}[];
carpetpoints: {x: number; y: number}[];
}
export interface PointsHistory {
floorpoints: {x: number; y: number}[];
notfloorpoints: {x: number; y: number}[];
inputSelected: string;
}
export interface CarpetRatio {
w: number;
h: number;
}
export interface ScaleFactor {
x: number;
y: number;
}
export interface Vector3Pool {
v1: THREE.Vector3;
v2: THREE.Vector3;
v3: THREE.Vector3;
v4: THREE.Vector3;
}
export interface Vector2Pool {
v1: THREE.Vector2;
v2: THREE.Vector2;
}

View File

@@ -1,2 +1,2 @@
import '../Components/RoomView'; import '../Components/RoomView/index.js';
import '../Components/RoomView/roomView-react.ts'; import '../react-wrapper/roomView-react.js';

317
src/Utils/canvasutils.ts Normal file
View File

@@ -0,0 +1,317 @@
import {readImage} from './domUtils';
declare const AppProvider: {
uploadRoomviewBlob: (params: {blob: Blob}) => Promise<string>;
postLogDetails: (params: {actionid: string; filename: string}) => void;
};
interface WindowExtended extends Window {
flutter_inappwebview?: {callHandler: (name: string, data: string) => void};
isNativeApp?: boolean;
flags: {inhouseChanges: {saveFunctionForFlutter?: boolean}};
getByteData?: string;
}
export const createCanvas = (w: number, h: number): HTMLCanvasElement => {
const canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
return canvas;
};
export const clearCanvas = (
canvas: HTMLCanvasElement,
w: number,
h: number
) => {
canvas.getContext('2d')?.clearRect(0, 0, w, h);
};
export function applyEdgeFilter(
canvas: HTMLCanvasElement,
image: HTMLImageElement
): HTMLCanvasElement {
const pix = 4;
const blur = '2px';
const brightness = '50%';
const {width: w, height: h} = canvas;
const designCtx = canvas.getContext('2d')!;
const filterCanvas = createCanvas(w, h);
const designfilterCtx = filterCanvas.getContext('2d')!;
designfilterCtx.filter = `blur(${blur}) brightness(${brightness})`;
designfilterCtx.drawImage(image, 0, 0, w, h);
designCtx.putImageData(designfilterCtx.getImageData(0, 0, pix, h), 0, 0);
designCtx.putImageData(designfilterCtx.getImageData(0, 0, w, pix), 0, 0);
designCtx.putImageData(
designfilterCtx.getImageData(0, h - pix, w, pix),
0,
h - pix
);
designCtx.putImageData(
designfilterCtx.getImageData(w - pix, 0, pix, h),
w - pix,
0
);
return canvas;
}
export function applyBWMask(
maskCanvas: HTMLCanvasElement,
imageUrl: string,
maskUrl: string,
callback: (canvas: HTMLCanvasElement) => void
): void {
const {width, height} = maskCanvas;
const tempCanvas = createCanvas(width, height);
const maskCtx = maskCanvas.getContext('2d')!;
const tempCtx = tempCanvas.getContext('2d')!;
const img = new Image();
img.src = imageUrl;
img.crossOrigin = 'Anonymous';
img.onload = () => {
maskCanvas.width = width;
maskCanvas.height = height;
maskCtx.drawImage(img, 0, 0, width, height);
const maskImg = new Image();
maskImg.src = maskUrl;
maskImg.crossOrigin = 'Anonymous';
maskImg.onload = () => {
tempCanvas.width = width;
tempCanvas.height = height;
tempCtx.drawImage(maskImg, 0, 0, width, height);
const imgData = maskCtx.getImageData(0, 0, width, height);
const maskData = tempCtx.getImageData(0, 0, width, height);
for (let i = 0; i < maskData.data.length; i += 4) {
imgData.data[i + 3] = 255 - maskData.data[i];
}
maskCtx.putImageData(imgData, 0, 0);
callback(maskCanvas);
};
};
}
export const applyMask = (
canvas: HTMLCanvasElement,
imgUrl: string,
maskUrl: string
): Promise<void> => {
const {width, height} = canvas;
const tempCanvas = createCanvas(width, height);
const ctx = canvas.getContext('2d')!;
const tempCtx = tempCanvas.getContext('2d')!;
return Promise.all([readImage(imgUrl), readImage(maskUrl)]).then(
([image, mask]) => {
ctx.drawImage(image, 0, 0, width, height);
tempCtx.drawImage(mask, 0, 0, width, height);
const imgData = ctx.getImageData(0, 0, width, height);
const maskData = tempCtx.getImageData(0, 0, width, height);
for (let i = 0; i < maskData.data.length; i += 4) {
imgData.data[i + 3] = 255 - maskData.data[i];
}
ctx.putImageData(imgData, 0, 0);
}
);
};
export const bnwToTransparency = (
canvas: HTMLCanvasElement,
bnwUrl: string,
amount: number
): void => {
const {width, height} = canvas;
const ctx = canvas.getContext('2d')!;
const image = new Image();
image.src = bnwUrl;
image.crossOrigin = 'Anonymous';
image.onload = function () {
ctx.drawImage(image, 0, 0, width, height);
const imageData = ctx.getImageData(0, 0, width, height);
for (let i = 0; i < imageData.data.length; i += 4) {
imageData.data[i + 3] = (255 - imageData.data[i]) * amount;
imageData.data[i] = 0;
imageData.data[i + 1] = 0;
imageData.data[i + 2] = 0;
}
ctx.putImageData(imageData, 0, 0);
};
};
export const resizeCanvas = (
canvas: HTMLCanvasElement,
width: number,
height: number
): void => {
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
};
export const downloadImageData = (
canvas: HTMLCanvasElement,
name: string,
mime: string
): void => {
const mimetype = mime === 'jpg' ? 'jpeg' : mime;
const win = window as unknown as WindowExtended;
const downloadBlob = (blob: Blob) => {
const url = URL.createObjectURL(blob);
const anchor = document.createElement('a');
anchor.href = url;
anchor.setAttribute('download', name);
const imageFileName = name.substring(0, name.lastIndexOf('.'));
if (win.flutter_inappwebview || win.isNativeApp) {
AppProvider.uploadRoomviewBlob({blob}).then((response) => {
win.flutter_inappwebview!.callHandler(
'downloadImage',
JSON.stringify({image: response, fileName: imageFileName, type: mime})
);
});
} else {
setTimeout(() => {
if ((navigator as any).msSaveOrOpenBlob) {
(navigator as any).msSaveOrOpenBlob(blob, name);
} else {
anchor.click();
}
}, 100);
}
AppProvider.postLogDetails({actionid: 'saveButtonClicked', filename: name});
};
const type = `image/${mimetype}`;
if (win.flags.inhouseChanges.saveFunctionForFlutter) {
win.getByteData = canvas.toDataURL(type, 0.95);
return;
}
if (canvas.toBlob) {
canvas.toBlob(
(blob) => {
if (blob) {
downloadBlob(blob);
}
},
type,
0.95
);
return;
}
downloadBlob(dataURItoBlob(canvas.toDataURL(type, 0.95)));
};
export const canvasToBlobPromise = (
canvas: HTMLCanvasElement,
imageQuality = 0.75
): Promise<Blob> =>
new Promise((resolve) => {
if (canvas.toBlob) {
canvas.toBlob((blob) => {
if (blob) resolve(blob);
});
return;
}
if ((canvas as any).msToBlob) {
resolve((canvas as any).msToBlob());
return;
}
resolve(dataURItoBlob(canvas.toDataURL('image/jpeg', imageQuality)));
});
export const dataURItoBlob = (dataURI: string): Blob => {
const byteString = atob(dataURI.split(',')[1]);
const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], {type: mimeString});
};
export const dataURLtoBlobURL = (dataurl: string): Promise<string> => {
return new Promise((resolve) => {
resolve(URL.createObjectURL(dataURItoBlob(dataurl)));
});
};
export const cropStitchCanvas = ({
origCanvas,
canvas,
}: {
origCanvas: HTMLCanvasElement;
canvas: HTMLCanvasElement;
}): void => {
const {width: canvasWidth, height: canvasHeight} = origCanvas;
const origCtx = origCanvas.getContext('2d')!;
const {width, height} = canvas;
const ctx = canvas.getContext('2d')!;
if (width === canvasWidth && height === canvasHeight) {
ctx.drawImage(origCanvas, 0, 0);
return;
}
const overlapSize = 20;
const overlapsx = canvasWidth - width / 2 - overlapSize;
const overlapsy = canvasHeight - height / 2 - overlapSize;
const wid = width / 2;
const hgt = height / 2;
const tl = origCtx.getImageData(0, 0, wid, hgt);
const tr = origCtx.getImageData(overlapsx, 0, wid + overlapSize, hgt);
const bl = origCtx.getImageData(0, overlapsy, wid, hgt + overlapSize);
const br = origCtx.getImageData(
overlapsx,
overlapsy,
wid + overlapSize,
hgt + overlapSize
);
const trCanvas = createCanvas(tr.width, tr.height);
const trCtx = trCanvas.getContext('2d')!;
trCtx.putImageData(tr, 0, 0);
trCtx.globalCompositeOperation = 'destination-out';
const trgrd = trCtx.createLinearGradient(0, 0, overlapSize, 0);
trgrd.addColorStop(0, 'black');
trgrd.addColorStop(1, 'transparent');
trCtx.fillStyle = trgrd;
trCtx.fillRect(0, 0, overlapSize, tr.height);
const blCanvas = createCanvas(bl.width, bl.height);
const blCt = blCanvas.getContext('2d')!;
blCt.putImageData(bl, 0, 0);
blCt.globalCompositeOperation = 'destination-out';
const blgrd = blCt.createLinearGradient(0, 0, 0, overlapSize);
blgrd.addColorStop(0, 'black');
blgrd.addColorStop(1, 'transparent');
blCt.fillStyle = blgrd;
blCt.fillRect(0, 0, bl.width, overlapSize);
const brCanvas = createCanvas(br.width, br.height);
const brCt = brCanvas.getContext('2d')!;
brCt.putImageData(br, 0, 0);
brCt.globalCompositeOperation = 'destination-out';
const brgrd = brCt.createLinearGradient(0, 0, 0, overlapSize);
brgrd.addColorStop(0, 'black');
brgrd.addColorStop(1, 'transparent');
brCt.fillStyle = brgrd;
brCt.fillRect(0, 0, br.width, overlapSize);
const brgrd1 = brCt.createLinearGradient(0, 0, overlapSize, 0);
brgrd1.addColorStop(0, 'black');
brgrd1.addColorStop(1, 'transparent');
brCt.fillStyle = brgrd1;
brCt.fillRect(0, 0, overlapSize, bl.height);
ctx.putImageData(tl, 0, 0);
ctx.drawImage(trCanvas, width - tr.width, 0);
ctx.drawImage(blCanvas, 0, height - bl.height);
ctx.drawImage(brCanvas, width - br.width, height - br.height);
};

264
src/Utils/domUtils.ts Normal file
View File

@@ -0,0 +1,264 @@
import {createCanvas} from './canvasutils';
import {xoroshiro128plus} from 'pure-rand/generator/xoroshiro128plus';
import {uniformInt} from 'pure-rand/distribution/uniformInt';
declare const AppProvider: {
domain: string;
};
export const downloadAsJSON = (object: unknown, name: string): void => {
const dataStr =
'data:text/json;charset=utf-8,' +
encodeURIComponent(JSON.stringify(object));
const dlAnchorElem = document.createElement('a');
dlAnchorElem.setAttribute('href', dataStr);
dlAnchorElem.setAttribute('download', `${name}.json`);
dlAnchorElem.click();
};
export const loadCssFile = ({cssUrl}: {cssUrl: string}): void => {
const link = document.createElement('link');
link.setAttribute('rel', 'stylesheet');
link.setAttribute('type', 'text/css');
link.setAttribute('href', cssUrl);
document.getElementsByTagName('head')[0].appendChild(link);
};
export function readImage(url: string | Blob): Promise<HTMLImageElement>;
export function readImage<T extends Record<string, unknown>>(
url: string | Blob,
i: T
): Promise<{image: HTMLImageElement} & T>;
export function readImage(
url: string | Blob,
i?: Record<string, unknown>
): Promise<
HTMLImageElement | ({image: HTMLImageElement} & Record<string, unknown>)
> {
const imageUrl = url instanceof Blob ? URL.createObjectURL(url) : url;
return new Promise((resolve, reject) => {
const image = new Image();
image.crossOrigin = 'Anonymous';
image.src = imageUrl;
image.onload = () => {
if (i) resolve({image, ...i});
else resolve(image);
};
image.onerror = reject;
});
}
export function scrollIntoViewIfNeeded(target: HTMLElement): void {
const rect = target.getBoundingClientRect();
if (rect.bottom > window.innerHeight) target.scrollIntoView(false);
if (rect.top < 0) target.scrollIntoView();
}
export const isIE = (): boolean =>
window.navigator.userAgent.indexOf('MSIE ') > 0 ||
!!navigator.userAgent.match(/Trident.*rv:11\./);
export function getJsonFromUrl(url?: string): Record<string, string> {
if (!url) url = window.location.search;
const query = url.substring(1);
const result: Record<string, string> = {};
query.split('&').forEach((part) => {
const item = part.split('=');
result[item[0]] = decodeURIComponent(item[1]);
});
return result;
}
export const getFromSessionStorage = (key: string): unknown => {
const item = sessionStorage.getItem(key);
if (item === null || item === 'undefined') return undefined;
if (item === 'null') return null;
try {
return JSON.parse(item);
} catch {
return item;
}
};
export const getCustomClass = (): string => {
return (getFromSessionStorage('customclass') as string) || '';
};
export const overlayTextureinCanvas = ({
image,
color,
canvasSize,
canvas,
}: {
image: CanvasImageSource;
color: string;
canvasSize: number;
canvas: HTMLCanvasElement;
}): void => {
const ctx = canvas.getContext('2d')!;
const seed = [...color].reduce((acc, c) => (acc * 31 + c.charCodeAt(0)) | 0, 0);
const start = uniformInt(xoroshiro128plus(seed), 0, 256 - canvasSize);
const can = createCanvas(
(image as HTMLImageElement).width,
(image as HTMLImageElement).height
);
can.getContext('2d')!.drawImage(image, 0, 0);
const imgData = can
.getContext('2d')!
.getImageData(start, start, canvasSize, canvasSize);
ctx.globalCompositeOperation = 'overlay';
can.width = canvasSize;
can.height = canvasSize;
can.getContext('2d')!.putImageData(imgData, 0, 0);
ctx.drawImage(can, 0, 0);
};
export const copyToClipboard = (str: string): void => {
const el = document.createElement('textarea');
el.value = str;
el.setAttribute('readonly', '');
el.style.position = 'absolute';
el.style.left = '-9999px';
document.body.appendChild(el);
const selection = document.getSelection();
const selected =
selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : false;
el.select();
document.execCommand('copy');
document.body.removeChild(el);
if (selected && selection) {
selection.removeAllRanges();
selection.addRange(selected);
}
};
const stopPrntScr = (): void => {
const inpFld = document.createElement('input');
inpFld.setAttribute('value', '.');
inpFld.setAttribute('width', '0');
inpFld.style.height = '0px';
inpFld.style.width = '0px';
inpFld.style.border = '0px';
document.body.appendChild(inpFld);
inpFld.select();
document.execCommand('copy');
inpFld.remove();
};
const accessClipboardData = (): void => {
try {
(window as any).clipboardData.setData('text', 'Access Restricted');
} catch {}
};
export const disableScreenshot = (): void => {
document.addEventListener('keyup', (e) => {
if (e.key === 'PrintScreen') stopPrntScr();
});
accessClipboardData();
};
export const inIframe = (): boolean => {
try {
return window.self !== window.top;
} catch {
return true;
}
};
interface SwipeData {
dir: string;
initial: number[];
velocity: number;
}
export const handleSwipeOnEdges = (
data: SwipeData | null | undefined
): {left?: boolean; right?: boolean} | undefined => {
if (!data) return;
const {dir: direction, initial, velocity} = data;
const startPos = initial[0];
if (velocity < 0.5) return;
const detectableArea =
window.innerWidth < 1000 ? window.innerWidth / 3 : window.innerWidth / 4;
const detectableArea_right = window.innerWidth - detectableArea;
let payload: {left?: boolean; right?: boolean} | undefined;
if (direction.toLowerCase() === 'right') {
if (startPos < detectableArea) payload = {left: true};
else if (startPos > detectableArea_right) payload = {right: false};
} else if (direction.toLowerCase() === 'left') {
if (startPos < detectableArea) payload = {left: false};
else if (startPos > detectableArea_right) payload = {right: true};
}
return payload;
};
export const loadPomUrl = (
color: string,
material: number
): Promise<string> => {
return new Promise((resolve, reject) => {
const matName = material === 1 ? 'silk' : 'wool';
const color_hex = color.replace('#', '');
const hex_r = color_hex.substring(0, 2);
const hex_g = color_hex.substring(2, 4);
const colorPomImageUrl = `${AppProvider.domain}/permanentcache/colorpoms/${matName}/${hex_r}/${hex_g}/${color_hex}.png`;
readImage(colorPomImageUrl)
.then(() => resolve(colorPomImageUrl))
.catch(reject);
});
};
type ScrollDirection = 'left' | 'right' | 'up' | 'down';
export const handleNavigation = ({
elem,
direction,
}: {
elem: HTMLElement | null;
direction: ScrollDirection;
}): void => {
if (!elem || !direction) return;
const isHorizontal = direction === 'left' || direction === 'right';
const isVertical = direction === 'up' || direction === 'down';
if (isHorizontal) {
const offsetValue =
direction === 'right'
? elem.offsetWidth / 2
: (-1 * elem.offsetWidth) / 2;
if (
Math.abs(elem.scrollLeft + elem.clientWidth - elem.scrollWidth) < 2 &&
direction === 'right'
) {
elem.scrollLeft = 0;
} else if (elem.scrollLeft === 0 && direction === 'left') {
elem.scrollLeft = elem.scrollWidth;
} else {
elem.scrollLeft += offsetValue;
}
}
if (isVertical) {
const offsetValue =
direction === 'down'
? elem.offsetHeight / 2
: (-1 * elem.offsetHeight) / 2;
if (
Math.abs(elem.scrollTop + elem.clientHeight - elem.scrollHeight) < 2 &&
direction === 'down'
) {
elem.scrollTop = 0;
} else if (elem.scrollTop === 0 && direction === 'up') {
elem.scrollTop = elem.scrollHeight;
} else {
elem.scrollTop += offsetValue;
}
}
};

736
src/Utils/utils.ts Normal file
View File

@@ -0,0 +1,736 @@
/* eslint-disable no-useless-escape */
import axios from 'axios';
import Pako from 'pako';
export interface Size {
width: number;
height: number;
}
interface SizedContainer {
offsetWidth: number;
offsetHeight: number;
}
export interface OrderSheetDetails {
PhysicalHeight: number;
PhysicalWidth: number;
Unit: string;
}
interface BoxSize {
Width: number;
Height: number;
}
interface BooleanExpression {
variable?: string;
condition?: string;
operands?: BooleanExpression[];
}
interface ColorItem {
ColorName: string;
Color: string;
}
interface PaletteItem {
Name: string;
Value: string;
}
interface Vector3Like {
x: number;
y: number;
project(camera: unknown): Vector3Like;
}
declare global {
interface Window {
flags: {
ordersheet?: {
showAreaInM2?: boolean;
showTotalAreaInYards?: boolean;
};
visualizations?: {
wallToWallCenterRepeat?: {x?: boolean; y?: boolean};
};
inhouseChanges?: {
saveFunctionForFlutter?: boolean;
};
};
TextureOptions: {
DefaultTextureNames: string[];
AdditionalTextureNames?: string[];
CustomTextureIndices: number[];
CustomTextureNames: string[];
};
}
}
export const convertUnits = (
value: number,
from: string,
to: string
): number => {
let convertedValue = value;
if (from === 'ft') {
if (to === 'in') convertedValue = value * 12;
else if (to === 'cm') convertedValue = value * 30.47999902464003;
} else if (from === 'in') {
if (to === 'ft') convertedValue = value / 12;
else if (to === 'cm') convertedValue = value * 2.5399999187200026;
} else if (from === 'cm') {
if (to === 'ft') convertedValue = value * 0.0328084;
else if (to === 'in') convertedValue = value * 0.3937008;
}
return convertedValue;
};
export function convertArrIntoRad(arrDeg: number[]): number[] {
return arrDeg.map((angle) => (angle * Math.PI) / 180);
}
export const convertArrintoDeg = (arrRad: number[]): number[] =>
arrRad.map((angle) => (angle * 180) / Math.PI);
export const degToRad = (deg: number): number => (deg * Math.PI) / 180;
export const radToDeg = (deg: number): number => (deg * 180) / Math.PI;
export function fitImageToContainer(image: Size, container: Size): Size {
let {width: containerwidth, height: containerheight} = container;
let {width: imagewidth, height: imageheight} = image;
let width = imagewidth,
height = imageheight;
if (imagewidth > imageheight) {
if (width > containerwidth) {
height = (imageheight * containerwidth) / imagewidth;
width = containerwidth;
}
if (height > containerheight) {
width = (imagewidth * containerheight) / imageheight;
height = containerheight;
}
} else {
if (height > containerheight) {
width = (imagewidth * containerheight) / imageheight;
height = containerheight;
}
if (width > containerwidth) {
height = (imageheight * containerwidth) / imagewidth;
width = containerwidth;
}
}
return {width, height};
}
export const readJSON = (url: string): Promise<unknown> =>
axios.get(url).then((response) => response.data as unknown);
export const shuffleArray = <T>(array: T[]): T[] => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
};
export const constructBooleanExpression = (data: unknown): string => {
if (!data || typeof data !== 'object') return '';
const operatorsMap: Record<string, string> = {
OR: '||',
AND: '&&',
LT: '<',
GT: '>',
EQ: '==',
NEQ: '!=',
GTE: '>=',
LTE: '<=',
};
function process(node: BooleanExpression): string {
if (node.variable) return node.variable;
if (node.operands) {
const operator = operatorsMap[node.condition ?? ''] ?? '';
return `(${node.operands.map(process).join(` ${operator} `)})`;
}
return '';
}
return process(data as BooleanExpression);
};
export const findSingleTrueCondition = (
conditions: BooleanExpression[],
variables: Record<string, string | number>
): number | null => {
let trueIndex = -1;
for (let i = 0; i < conditions.length; i++) {
const expression = constructBooleanExpression(
conditions[i].condition ? conditions[i] : conditions[i].operands?.[0]
);
const filledExpression = expression.replace(/\b\w+\b/g, (match) =>
variables[match] !== undefined ? String(variables[match]) : match
);
// eslint-disable-next-line no-eval
const isTrue = eval(filledExpression);
if (isTrue) return i;
}
if (trueIndex === -1) return null;
return trueIndex;
};
export function fitIntoContainer(image: Size, container: SizedContainer): Size {
const {offsetWidth: containerwidth, offsetHeight: containerheight} =
container;
const imagewidth = image.width,
imageheight = image.height;
if (!image.width || !image.height)
return {width: containerwidth, height: containerheight};
let width = imagewidth,
height = imageheight;
const wdif = width - containerwidth;
const hdif = height - containerheight;
if (imagewidth > imageheight) {
if (wdif > hdif) {
height = containerheight;
width = (imagewidth * containerheight) / imageheight;
} else {
width = containerwidth;
height = (imageheight * containerwidth) / imagewidth;
}
}
return {width, height};
}
export function resizeKeepingAspect(
image: Size,
container: Size,
fitType = 'fit_inside',
resolution = 1
): {width: number; height: number; xOffset: number; yOffset: number} {
let {width: containerwidth, height: containerheight} = container;
let {width: imagewidth, height: imageheight} = image;
if (!imagewidth || !imageheight)
return {
width: containerwidth,
height: containerheight,
xOffset: 0,
yOffset: 0,
};
if (containerheight === 0 || containerwidth === 0)
return {width: imagewidth, height: imageheight, xOffset: 0, yOffset: 0};
let width = imagewidth,
height = imageheight;
let xOffset = 0;
let yOffset = 0;
switch (fitType) {
case 'fit_inside':
if (imagewidth > imageheight) {
if (width > containerwidth) {
height = (imageheight * containerwidth) / imagewidth;
width = containerwidth;
}
if (height > containerheight) {
width = (imagewidth * containerheight) / imageheight;
height = containerheight;
}
} else {
if (height > containerheight) {
width = (imagewidth * containerheight) / imageheight;
height = containerheight;
}
if (width > containerwidth) {
height = (imageheight * containerwidth) / imagewidth;
width = containerwidth;
}
}
break;
case 'crop': {
const wdif = width - containerwidth;
const hdif = height - containerheight;
if (wdif > hdif) {
height = containerheight;
width = (imagewidth * containerheight) / imageheight;
} else {
width = containerwidth;
height = (imageheight * containerwidth) / imagewidth;
}
xOffset = (containerwidth - width) / 2;
yOffset = (containerheight - height) / 2;
break;
}
default:
break;
}
width = width * resolution;
height = height * resolution;
return {width, height, xOffset, yOffset};
}
export const convertUnit = (
from: string,
to: string,
value: number,
fixed?: number,
customConversion = false,
customInToCmFactor = 2.5
): number => {
let converted = convertUnits(value, from, to);
if (customConversion) {
if (from === 'in' && to === 'cm') converted = value * customInToCmFactor;
else if (from === 'cm' && to === 'in')
converted = value / customInToCmFactor;
else if (from === 'ft' && to === 'cm')
converted = value * customInToCmFactor * 12;
else if (from === 'cm' && to === 'ft')
converted = value / (customInToCmFactor * 12);
}
if (fixed) return Number(converted.toFixed(fixed));
return converted;
};
export const convertUnit_Arr = (
from: string,
to: string,
valueArr: number[],
setCustomConversion = false,
customInToCmFactor?: number
): number[] =>
valueArr.map((val) =>
convertUnit(from, to, val, 2, setCustomConversion, customInToCmFactor)
);
export const getExtension = (path: string): string => {
const fp = path.split('.');
return fp[fp.length - 1];
};
export function isiPhone(): boolean {
return (
navigator.platform.indexOf('iPhone') !== -1 ||
navigator.platform.indexOf('iPad') !== -1 ||
navigator.platform.indexOf('iPod') !== -1
);
}
export function convertNumberToFeetInch(
f: number,
unit: string
): number | string {
if (unit !== 'ft') return f;
let ft = Math.floor(f);
let inch = Math.round(12 * (f - ft));
if (inch === 12) {
ft++;
inch = 0;
}
return ft + '' + (inch > 0 ? inch + '″' : '');
}
export function convertFeetInchToNumber(
f: string | number,
unit: string
): number | null {
if (unit !== 'ft') return typeof f === 'number' ? f : parseFloat(f) || null;
const rex = /[-+]?[0-9]*\.?[0-9]+/g;
const match = String(f).match(rex);
if (match) {
const feet = parseFloat(match[0]);
const inch = match.length > 1 ? parseFloat(match[1]) : 0;
if (feet > 0 && inch >= 0 && inch < 12) return feet + inch / 12;
}
return null;
}
export const displayAreaOfRug = (
orderSheetDetails: OrderSheetDetails
): number => {
let displayArea =
orderSheetDetails.PhysicalHeight * orderSheetDetails.PhysicalWidth;
if (orderSheetDetails.Unit === 'cm') displayArea = displayArea / 10000;
return displayArea;
};
export const displayAreaInFt = (
orderSheetDetails: OrderSheetDetails,
setCustomConversion?: boolean,
customInToCmFactor?: number
): number => {
let displayArea = displayAreaOfRug(orderSheetDetails);
if (orderSheetDetails.Unit === 'cm') {
const [PhysicalWidth, PhysicalHeight] = convertUnit_Arr(
orderSheetDetails.Unit,
'ft',
[orderSheetDetails.PhysicalWidth, orderSheetDetails.PhysicalHeight],
setCustomConversion,
customInToCmFactor
);
displayArea = PhysicalWidth * PhysicalHeight;
}
return displayArea;
};
export const displayUnitOfRug = (unit: string): string =>
unit === 'cm' ? 'm' : unit;
export const calculateTotalArea = (
orderSheetDetails: OrderSheetDetails
): string => {
const unit = orderSheetDetails.Unit;
let totalArea = `${
Math.round(displayAreaOfRug(orderSheetDetails) * 100) / 100
} sq. ${displayUnitOfRug(unit)}.`;
if (window.flags.ordersheet?.showAreaInM2) {
totalArea = `${
Math.round(displayAreaOfRug(orderSheetDetails) * 100) / 100
} ${displayUnitOfRug(
unit
)}<span class = "at-ordersheet-area-superscript">&#178;</span>`;
}
if (window.flags.ordersheet?.showTotalAreaInYards)
totalArea = `${
Math.round((displayAreaOfRug(orderSheetDetails) / 9) * 100) / 100
} sq. Yds. `;
return totalArea;
};
export function makeid(length: number): string {
let result = '';
const characters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
export const leftFillNum = (num: number, targetLength: number): string =>
num.toString().padStart(targetLength, '0');
export const convertTilePointToName = (i: number, j: number): string =>
`${leftFillNum(i, 2)}_${leftFillNum(j, 2)}`;
export const convertNameToTilePoint = (
name: string
): {x: number; y: number; name: string} => {
const x = parseInt(name.trim().substring(0, 2));
const y = parseInt(name.trim().substring(3, 5));
return {x, y, name};
};
export const getPathFromString = (string: string): string => {
const x = string.split('/');
x.pop();
return x.join('/');
};
export const createAsyncQueue = (
tasks: Promise<unknown>[],
maxNumOfWorkers = 5
): Promise<unknown[]> => {
let numOfWorkers = 0;
let taskIndex = 0;
return new Promise((done) => {
const handleResult = (index: number) => (result: unknown) => {
tasks[index] = Promise.resolve(result);
numOfWorkers--;
getNextTask();
};
const getNextTask = () => {
if (numOfWorkers < maxNumOfWorkers && taskIndex < tasks.length) {
tasks[taskIndex]
.then(handleResult(taskIndex))
.catch(handleResult(taskIndex));
taskIndex++;
numOfWorkers++;
getNextTask();
} else if (numOfWorkers === 0 && taskIndex === tasks.length) {
done(tasks);
}
};
getNextTask();
});
};
export const areaOfellipse = (x: number, y: number): number => Math.PI * x * y;
export const createVector = (
p: Vector3Like,
camera: unknown,
width: number,
height: number
): Vector3Like => {
const vector = p.project(camera);
vector.x = ((vector.x + 1) / 2) * width;
vector.y = (-(vector.y - 1) / 2) * height;
return vector;
};
export const getDesignPathInTitle = (designPath: string): string => {
if (!designPath) return '';
const dotPos = designPath.lastIndexOf('.');
const trimmed = designPath.substring(0, dotPos);
return trimmed.replace(/\/\./g, '/').split('/').slice(1).join('/');
};
export const isIos: boolean =
!!navigator.userAgent.match(/Safari/i) &&
!navigator.userAgent.match(/Chrome/i);
export const isAppleDevice: boolean = navigator.userAgent.includes('Mac');
export const isTouchScreen: boolean = navigator.maxTouchPoints >= 1;
export const isMobileDevice: boolean =
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iPhone|iPod|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)vodafone|wap|windows (ce|phone)|xda|xiino/i.test(
navigator.userAgent
) ||
(isAppleDevice && isTouchScreen) ||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
navigator.userAgent.substr(0, 4)
);
export const shuffle = <T>(arra1: T[]): T[] => {
let ctr = arra1.length;
let temp: T;
let index: number;
while (ctr > 0) {
index = Math.floor(Math.random() * ctr);
ctr--;
temp = arra1[ctr];
arra1[ctr] = arra1[index];
arra1[index] = temp;
}
return arra1;
};
export const shuffleForLocked = <T>(
arra1: T[],
temparr: number[] = []
): T[] => {
let temp: T;
let index: number;
const unlockedArray: T[] = [];
arra1.forEach((item, i) => {
if (!temparr.includes(i)) unlockedArray.push(item);
});
let ctr = unlockedArray.length;
if (ctr === 2) {
const temp1 = unlockedArray[0];
unlockedArray[0] = unlockedArray[1];
unlockedArray[1] = temp1;
} else {
while (ctr > 0) {
index = Math.floor(Math.random() * ctr);
ctr--;
temp = unlockedArray[ctr];
unlockedArray[ctr] = unlockedArray[index];
unlockedArray[index] = temp;
}
}
let unctr = 0;
arra1.forEach((_item, i) => {
if (!temparr.includes(i)) {
arra1.splice(i, 1, unlockedArray[unctr]);
unctr++;
}
});
return arra1;
};
export const getPalette = (arra1: ColorItem[]): PaletteItem[] => {
let ctr = arra1.length;
const newColorPalette: PaletteItem[] = new Array(ctr);
while (ctr > 0) {
ctr--;
newColorPalette[ctr] = {
Name: arra1[ctr].ColorName,
Value: arra1[ctr].Color,
};
}
return newColorPalette;
};
export const safelyParseJSON = (json: string): unknown => {
let parsed: unknown;
try {
parsed = JSON.parse(json);
} catch {
// intentionally empty
}
return parsed;
};
export const decodeColorsFromString = (str: string): unknown => {
try {
const binaryStr = window.atob(str);
const bytes = Uint8Array.from(binaryStr, (c) => c.charCodeAt(0));
return safelyParseJSON(Pako.inflate(bytes, {to: 'string'}));
} catch (exception) {
console.error(exception);
return null;
}
};
export const encodeColorsToString = (colors: unknown): string => {
const compressed = Pako.deflate(JSON.stringify(colors));
const binaryStr = Array.from(compressed, (c) => String.fromCharCode(c)).join('');
return window.btoa(binaryStr);
};
export function getMaterialNameArray(): string[] {
const materialArr: string[] = new Array(70).fill('');
window.TextureOptions.DefaultTextureNames.forEach((name, i) => {
materialArr[i] = name;
});
if (window.TextureOptions.AdditionalTextureNames) {
window.TextureOptions.AdditionalTextureNames.forEach((name, i) => {
materialArr[i + 4] = name;
});
}
window.TextureOptions.CustomTextureIndices.forEach((texIndex, i) => {
materialArr[texIndex] = window.TextureOptions.CustomTextureNames[i];
});
return materialArr;
}
export const errorTypes = {
INTERFACE_ERROR: {message: 'INTERFACE_ERROR'},
ANICENT_BROWSER: {message: 'ANICENT_BROWSER'},
NO_CONNECTION: {message: 'NO_CONNECTION'},
INVALID_LINK: {message: 'INVALID_LINK'},
SESSION_TIMEOUT: {message: 'SESSION_TIMEOUT'},
URL_INCOMPLETE: {message: 'URL_INCOMPLETE'},
};
export const toastErrors = {
NO_CONNECTION: {
message: 'Looks like you got disconnected. Check your internet connection.',
},
SERVER_ERROR: {
message: 'Were having some issues. Please try again in a while.',
},
};
export const doesStringMatch = (string: string, searchKey: string): boolean =>
string.toLowerCase().indexOf(searchKey.toLowerCase()) !== -1;
export const replaceAt = (
string: string,
index: number,
replace: string
): string => string.substring(0, index) + replace + string.substring(index + 1);
export const replaceAll = (
string: string,
replace: string,
replaceWith: string
): string => string.replace(new RegExp(replace, 'g'), replaceWith);
export function makeUrl(
...args: (string | number | null | undefined | false)[]
): string {
let res = '';
args.forEach((argument, index) => {
if (argument) {
res = `${res}${index === 0 ? '' : '/'}${argument}`;
}
});
return res;
}
export function getCroppedSize(
srcSize: BoxSize,
dstSize: BoxSize,
cropPadding = 0
): {width: number; height: number; offsetX: number; offsetY: number} {
let width = dstSize.Width,
height = dstSize.Height;
const {Width: canvasWidth, Height: canvasHeight} = srcSize;
const wr = dstSize.Width / canvasWidth;
const hr = dstSize.Height / canvasHeight;
if (wr > hr) {
if (dstSize.Width > canvasWidth) {
width = canvasWidth;
height = (dstSize.Height * width) / dstSize.Width;
}
} else {
if (dstSize.Height > canvasHeight) {
height = canvasHeight;
width = (dstSize.Width * height) / dstSize.Height;
}
}
const shouldApplyPad =
(width / height).toFixed(1) !== (canvasWidth / canvasHeight).toFixed(1);
if (shouldApplyPad) {
width = width - cropPadding;
height = height - cropPadding;
}
width = Math.ceil(width);
height = Math.ceil(height);
const offsetX = (width - srcSize.Width) / 2;
const offsetY = (height - srcSize.Height) / 2;
return {width, height, offsetX, offsetY};
}
export const getDesignDimensionsString = ({
designDetails,
showOneDimensionIfSquare = false,
showDesignDimensions = true as boolean | number,
showDimensionInInches = false,
}: {
designDetails: OrderSheetDetails;
showOneDimensionIfSquare?: boolean;
showDesignDimensions?: boolean | number;
showDimensionInInches?: boolean;
}): string => {
const {PhysicalHeight, PhysicalWidth, Unit} = designDetails;
const widthString = convertNumberToFeetInch(PhysicalWidth, Unit);
const heightString = ` x ${convertNumberToFeetInch(PhysicalHeight, Unit)}`;
const combinedStr = `${widthString}${
showOneDimensionIfSquare && PhysicalHeight === PhysicalWidth
? ''
: heightString
}`;
let designDimensionsString = `${combinedStr} ${Unit === 'ft' ? '' : Unit}`;
if (showDesignDimensions === 2 && Unit) {
const unit = Unit === 'ft' ? 'cm' : 'ft';
const wid = convertUnit(Unit, unit, PhysicalWidth, 2) || PhysicalWidth;
const hgt = convertUnit(Unit, unit, PhysicalHeight, 2) || PhysicalHeight;
const widthStr = convertNumberToFeetInch(wid, unit);
const heightStr = ` x ${convertNumberToFeetInch(hgt, unit)}`;
const string_unit = `${widthStr}${
showOneDimensionIfSquare && PhysicalHeight === PhysicalWidth
? ''
: heightStr
} ${unit === 'ft' ? '' : unit}`;
designDimensionsString = `${designDimensionsString} (${string_unit})`;
}
if (showDimensionInInches && PhysicalWidth && PhysicalHeight) {
const wid = convertUnit(Unit, 'in', PhysicalWidth, 2);
const hgt = convertUnit(Unit, 'in', PhysicalHeight, 2);
designDimensionsString = `${wid}″ x ${hgt}`;
}
return designDimensionsString;
};
export const createUriSafe = (uriString: string): string =>
uriString.split('/').map(encodeURIComponent).join('/');
export const calculateKLratio = ({
Height,
PhysicalHeight,
Width,
PhysicalWidth,
}: {
Height: number;
PhysicalHeight: number;
Width: number;
PhysicalWidth: number;
}): number => (Width * PhysicalHeight) / (PhysicalWidth * Height);

View File

@@ -0,0 +1,15 @@
// react-wrapper/index.ts
import {createComponent} from '@lit/react';
import React from 'react';
import {RoomIllustration} from '../Components/RoomView';
export const RoomIllustrationReact = createComponent({
react: React,
tagName: 'room-illustration',
elementClass: RoomIllustration,
events: {
onRenderingComplete: 'rendering-complete',
onSizesChange: 'sizes-change',
onActiveClick: 'active-click',
},
});

View File

@@ -16,7 +16,7 @@
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noImplicitAny": true, "noImplicitAny": true,
"noImplicitThis": true, "noImplicitThis": true,
"moduleResolution": "node", "moduleResolution": "bundler",
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
@@ -29,6 +29,6 @@
], ],
"types": ["mocha"] "types": ["mocha"]
}, },
"include": ["src/**/*.ts"], "include": ["src/**/*.ts", "src/Components/RoomView/helpers/RoomViewHelper.js"],
"exclude": [] "exclude": []
} }

120
yarn.lock
View File

@@ -913,6 +913,11 @@
dependencies: dependencies:
es-module-lexer "^0.9.3" es-module-lexer "^0.9.3"
"@dimforge/rapier3d-compat@~0.12.0":
version "0.12.0"
resolved "https://registry.yarnpkg.com/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz#7b3365e1dfdc5cd957b45afe920b4ac06c7cd389"
integrity sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==
"@esbuild/aix-ppc64@0.21.5": "@esbuild/aix-ppc64@0.21.5":
version "0.21.5" version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
@@ -1592,6 +1597,11 @@
resolved "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz" resolved "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz"
integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==
"@tweenjs/tween.js@~23.1.3":
version "23.1.3"
resolved "https://registry.yarnpkg.com/@tweenjs/tween.js/-/tween.js-23.1.3.tgz#eff0245735c04a928bb19c026b58c2a56460539d"
integrity sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==
"@types/accepts@*": "@types/accepts@*":
version "1.3.7" version "1.3.7"
resolved "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz" resolved "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz"
@@ -1787,6 +1797,11 @@
dependencies: dependencies:
undici-types "~7.19.0" undici-types "~7.19.0"
"@types/pako@^2.0.4":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@types/pako/-/pako-2.0.4.tgz#c3575ef8125e176c345fa0e7b301c1db41170c15"
integrity sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==
"@types/parse5@^6.0.1": "@types/parse5@^6.0.1":
version "6.0.3" version "6.0.3"
resolved "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz" resolved "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz"
@@ -1867,6 +1882,23 @@
resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-15.0.1.tgz" resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-15.0.1.tgz"
integrity sha512-Ko2tjWJq8oozHzHV+reuvS5KYIRAokHnGbDwGh/J64LntgpbuylF74ipEL24HCyRjf9FOlBiBHWBR1RlVKsI1w== integrity sha512-Ko2tjWJq8oozHzHV+reuvS5KYIRAokHnGbDwGh/J64LntgpbuylF74ipEL24HCyRjf9FOlBiBHWBR1RlVKsI1w==
"@types/stats.js@*":
version "0.17.4"
resolved "https://registry.yarnpkg.com/@types/stats.js/-/stats.js-0.17.4.tgz#1933e5ff153a23c7664487833198d685c22e791e"
integrity sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==
"@types/three@^0.184.0":
version "0.184.0"
resolved "https://registry.yarnpkg.com/@types/three/-/three-0.184.0.tgz#f6a3ea283bebb65e5de6ff4dbf18ef57bdcf5bcc"
integrity sha512-4mY2tZAu0y0B0567w7013BBXSpsP0+Z48NJvmNo4Y/Pf76yCyz6Jw4P3tUVs10WuYNXXZ+wmHyGWpCek3amJxA==
dependencies:
"@dimforge/rapier3d-compat" "~0.12.0"
"@tweenjs/tween.js" "~23.1.3"
"@types/stats.js" "*"
"@types/webxr" ">=0.5.17"
fflate "~0.8.2"
meshoptimizer "~1.1.1"
"@types/trusted-types@^2.0.2": "@types/trusted-types@^2.0.2":
version "2.0.7" version "2.0.7"
resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz" resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz"
@@ -1877,6 +1909,11 @@
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.15.10.tgz#742b77ec34d58554b94a76a14cef30d59e3c16b9" resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.15.10.tgz#742b77ec34d58554b94a76a14cef30d59e3c16b9"
integrity sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA== integrity sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==
"@types/webxr@>=0.5.17":
version "0.5.24"
resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.24.tgz#734d5d90dadc5809a53e422726c60337fa2f4a44"
integrity sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==
"@types/ws@^7.4.0": "@types/ws@^7.4.0":
version "7.4.7" version "7.4.7"
resolved "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz" resolved "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz"
@@ -2624,6 +2661,11 @@ async@^3.2.6:
resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz" resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz"
integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
atob@^2.1.2: atob@^2.1.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz" resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz"
@@ -2648,6 +2690,15 @@ axios@0.21.4:
dependencies: dependencies:
follow-redirects "^1.14.0" follow-redirects "^1.14.0"
axios@^1.15.2:
version "1.15.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.15.2.tgz#eb8fb6d30349abace6ade5b4cb4d9e8a0dc23e5b"
integrity sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==
dependencies:
follow-redirects "^1.15.11"
form-data "^4.0.5"
proxy-from-env "^2.1.0"
babel-plugin-polyfill-corejs2@^0.4.15: babel-plugin-polyfill-corejs2@^0.4.15:
version "0.4.17" version "0.4.17"
resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz" resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz"
@@ -3201,6 +3252,13 @@ color-name@~1.1.4:
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
command-line-args@5.1.2, command-line-args@^5.1.1: command-line-args@5.1.2, command-line-args@^5.1.1:
version "5.1.2" version "5.1.2"
resolved "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.2.tgz" resolved "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.2.tgz"
@@ -3540,6 +3598,11 @@ define-property@^2.0.2:
is-descriptor "^1.0.2" is-descriptor "^1.0.2"
isobject "^3.0.1" isobject "^3.0.1"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
delegates@^1.0.0: delegates@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz"
@@ -3828,6 +3891,16 @@ es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
dependencies: dependencies:
es-errors "^1.3.0" es-errors "^1.3.0"
es-set-tostringtag@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
dependencies:
es-errors "^1.3.0"
get-intrinsic "^1.2.6"
has-tostringtag "^1.0.2"
hasown "^2.0.2"
esbuild@^0.21.3: esbuild@^0.21.3:
version "0.21.5" version "0.21.5"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz"
@@ -4143,6 +4216,11 @@ fd-slicer@~1.1.0:
dependencies: dependencies:
pend "~1.2.0" pend "~1.2.0"
fflate@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea"
integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==
file-entry-cache@^6.0.1: file-entry-cache@^6.0.1:
version "6.0.1" version "6.0.1"
resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz"
@@ -4229,7 +4307,7 @@ flatted@^3.2.9:
resolved "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz" resolved "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz"
integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA== integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==
follow-redirects@^1.0.0, follow-redirects@^1.14.0: follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.15.11:
version "1.16.0" version "1.16.0"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz"
integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw== integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==
@@ -4254,6 +4332,17 @@ foreground-child@^3.1.0:
cross-spawn "^7.0.6" cross-spawn "^7.0.6"
signal-exit "^4.0.1" signal-exit "^4.0.1"
form-data@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053"
integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
es-set-tostringtag "^2.1.0"
hasown "^2.0.2"
mime-types "^2.1.12"
fragment-cache@^0.2.1: fragment-cache@^0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz"
@@ -4324,7 +4413,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5:
resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz"
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
@@ -5620,6 +5709,11 @@ merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1:
resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
meshoptimizer@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/meshoptimizer/-/meshoptimizer-1.1.1.tgz#20c7d050d59bdc573154814f6a37d47d89908cca"
integrity sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==
micromatch@^3.1.10: micromatch@^3.1.10:
version "3.1.10" version "3.1.10"
resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz" resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz"
@@ -5652,7 +5746,7 @@ mime-db@1.52.0:
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.18, mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35" version "2.1.35"
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
@@ -6063,6 +6157,11 @@ package-json-from-dist@^1.0.0:
resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz" resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz"
integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
pako@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86"
integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==
parent-module@^1.0.0: parent-module@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz"
@@ -6275,6 +6374,11 @@ proxy-from-env@1.1.0:
resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
proxy-from-env@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba"
integrity sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==
prr@~1.0.1: prr@~1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz" resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz"
@@ -6413,6 +6517,11 @@ puppeteer-core@^19.8.1:
unbzip2-stream "1.4.3" unbzip2-stream "1.4.3"
ws "8.13.0" ws "8.13.0"
pure-rand@^8.4.0:
version "8.4.0"
resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-8.4.0.tgz#1d9e26e9c0555486e08ae300d02796af8dec1cd0"
integrity sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A==
qs@^6.5.2: qs@^6.5.2:
version "6.15.1" version "6.15.1"
resolved "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz" resolved "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz"
@@ -7292,6 +7401,11 @@ text-table@^0.2.0:
resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
three@^0.184.0:
version "0.184.0"
resolved "https://registry.yarnpkg.com/three/-/three-0.184.0.tgz#5bca0a3851eea5345e4c205567b40dfa49b791b5"
integrity sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==
throttleit@^2.1.0: throttleit@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-2.1.0.tgz#a7e4aa0bf4845a5bd10daa39ea0c783f631a07b4" resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-2.1.0.tgz#a7e4aa0bf4845a5bd10daa39ea0c783f631a07b4"