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:
@@ -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",
|
||||||
|
|||||||
0
src/Components/RoomView/helpers/PhotographicView.ts
Normal file
0
src/Components/RoomView/helpers/PhotographicView.ts
Normal file
1780
src/Components/RoomView/helpers/RoomViewHelper.ts
Normal file
1780
src/Components/RoomView/helpers/RoomViewHelper.ts
Normal file
File diff suppressed because it is too large
Load Diff
0
src/Components/RoomView/helpers/ThreeViewHelper.ts
Normal file
0
src/Components/RoomView/helpers/ThreeViewHelper.ts
Normal 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>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
|
||||||
});
|
|
||||||
75
src/Components/RoomView/types.ts
Normal file
75
src/Components/RoomView/types.ts
Normal 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;
|
||||||
|
}
|
||||||
@@ -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
317
src/Utils/canvasutils.ts
Normal 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
264
src/Utils/domUtils.ts
Normal 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
736
src/Utils/utils.ts
Normal 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">²</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: 'We’re 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);
|
||||||
15
src/react-wrapper/roomView-react.ts
Normal file
15
src/react-wrapper/roomView-react.ts
Normal 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',
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -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
120
yarn.lock
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user