Compare commits
17 Commits
34e5d40cae
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 34cb733ed7 | |||
| 20618725d5 | |||
| d55b1cb9b7 | |||
| 12546d02c4 | |||
| 2100daadeb | |||
| 046280e25c | |||
| 8bcd4a014c | |||
| 5843ffdc9b | |||
| ab2f477b7f | |||
| 0496a48623 | |||
| e089dfabec | |||
| 4904f4d66a | |||
| 9936cd0ffc | |||
| e3e95fd05f | |||
| 3e3ea34e5f | |||
| 00f9c602af | |||
| 9ada6f005f |
@@ -5,13 +5,13 @@ import { fabric } from "fabric";
|
|||||||
import {
|
import {
|
||||||
GrayMaterial,
|
GrayMaterial,
|
||||||
PerforatedMaterial,
|
PerforatedMaterial,
|
||||||
WoodgrainMaterial
|
WoodgrainMaterial,
|
||||||
} from "Assets/images";
|
} from "Assets/images";
|
||||||
import {
|
import {
|
||||||
DockPanelCategories,
|
DockPanelCategories,
|
||||||
MaterialType,
|
MaterialType,
|
||||||
DockPanelCategoryMap,
|
DockPanelCategoryMap,
|
||||||
CylinderType
|
CylinderType,
|
||||||
} from "Utils/constants";
|
} from "Utils/constants";
|
||||||
import { Chevron } from "Assets/svgs";
|
import { Chevron } from "Assets/svgs";
|
||||||
import MkdSDK from "Utils/MkdSDK";
|
import MkdSDK from "Utils/MkdSDK";
|
||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
getMaterial,
|
getMaterial,
|
||||||
getRampsCategory,
|
getRampsCategory,
|
||||||
getWedgesAndRampsMaterial,
|
getWedgesAndRampsMaterial,
|
||||||
getWedgesCategory
|
getWedgesCategory,
|
||||||
} from "Utils/utils";
|
} from "Utils/utils";
|
||||||
|
|
||||||
const sdk = new MkdSDK();
|
const sdk = new MkdSDK();
|
||||||
@@ -32,7 +32,7 @@ export const Builder = ({ editor }) => {
|
|||||||
DockPanelCategories.RollIn
|
DockPanelCategories.RollIn
|
||||||
);
|
);
|
||||||
const rampsInitialState = {
|
const rampsInitialState = {
|
||||||
data: []
|
data: [],
|
||||||
};
|
};
|
||||||
// const [ramps, setRamps] = useState(null);
|
// const [ramps, setRamps] = useState(null);
|
||||||
const [activeLiftRange, setActiveLiftRange] = useState(null);
|
const [activeLiftRange, setActiveLiftRange] = useState(null);
|
||||||
@@ -44,7 +44,7 @@ export const Builder = ({ editor }) => {
|
|||||||
wedges: [],
|
wedges: [],
|
||||||
ramps: [],
|
ramps: [],
|
||||||
selectedRamps: [],
|
selectedRamps: [],
|
||||||
selectedWedges: []
|
selectedWedges: [],
|
||||||
});
|
});
|
||||||
const [boatLift, setBoatLift] = useState([]);
|
const [boatLift, setBoatLift] = useState([]);
|
||||||
const [liftRanges, setLiftRanges] = useState([]);
|
const [liftRanges, setLiftRanges] = useState([]);
|
||||||
@@ -60,56 +60,80 @@ export const Builder = ({ editor }) => {
|
|||||||
[activeMaterial]
|
[activeMaterial]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onDockSelect = useCallback((dock) => {
|
const onDockSelect = useCallback(
|
||||||
if (!editor) {
|
(dock) => {
|
||||||
return;
|
if (!editor) {
|
||||||
}
|
return;
|
||||||
const editorHeight = editor.getHeight();
|
}
|
||||||
const division = editorHeight / oneFeet - 4;
|
const editorHeight = editor.getHeight();
|
||||||
|
const division = editorHeight / oneFeet - 4;
|
||||||
|
|
||||||
let imageTopViewURL;
|
let imageTopViewURL;
|
||||||
let materials;
|
let materials;
|
||||||
let category;
|
let category;
|
||||||
if (["wedges", "ramps"].includes(dock?.type)) {
|
if (["wedges", "ramps"].includes(dock?.type)) {
|
||||||
imageTopViewURL = (dock?.top_view).replace("%20", "+");
|
imageTopViewURL = (dock?.top_view).replace("%20", "+");
|
||||||
|
materials = getWedgesAndRampsMaterial(dock?.material);
|
||||||
|
} else {
|
||||||
|
imageTopViewURL = dock?.top_view;
|
||||||
|
materials = getMaterial(dock?.materials);
|
||||||
|
}
|
||||||
|
|
||||||
materials = getWedgesAndRampsMaterial(dock?.material);
|
if (["ramps"].includes(dock?.type)) {
|
||||||
} else {
|
category = getRampsCategory(dock?.category);
|
||||||
imageTopViewURL = dock?.top_view;
|
} else if (["wedges"].includes(dock?.type)) {
|
||||||
materials = getMaterial(dock?.materials);
|
category = getWedgesCategory(dock?.category);
|
||||||
}
|
} else {
|
||||||
|
category = getCategory(dock?.category);
|
||||||
|
}
|
||||||
|
|
||||||
if (["ramps"].includes(dock?.type)) {
|
const dockData = {
|
||||||
category = getRampsCategory(dock?.category);
|
itemName: activeDockCategory,
|
||||||
} else if (["wedges"].includes(dock?.type)) {
|
image: dock?.image,
|
||||||
category = getWedgesCategory(dock?.category);
|
category: category,
|
||||||
} else {
|
length: dock?.length,
|
||||||
category = getCategory(dock?.category);
|
materials: materials,
|
||||||
}
|
top_view: dock?.top_view,
|
||||||
|
width: dock?.width,
|
||||||
|
lift_range: dock?.lift_range,
|
||||||
|
model: dock?.model,
|
||||||
|
no_of_cylinders: dock?.no_of_cylinders,
|
||||||
|
name: dock?.name,
|
||||||
|
thumbnail: dock?.thumbnail,
|
||||||
|
weight_capacity: dock?.weight_capacity,
|
||||||
|
};
|
||||||
|
|
||||||
const dockData = {
|
// Add dock to editor as an image
|
||||||
itemName: activeDockCategory,
|
const imageUrl = imageTopViewURL || dock?.image || dock?.thumbnail;
|
||||||
image: dock?.image,
|
|
||||||
category: category,
|
|
||||||
length: dock?.length,
|
|
||||||
materials: materials,
|
|
||||||
top_view: dock?.top_view,
|
|
||||||
width: dock?.width,
|
|
||||||
lift_range: dock?.lift_range,
|
|
||||||
model: dock?.model,
|
|
||||||
no_of_cylinders: dock?.no_of_cylinders,
|
|
||||||
name: dock?.name,
|
|
||||||
thumbnail: dock?.thumbnail,
|
|
||||||
weight_capacity: dock?.weight_capacity
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Add dock to editor
|
if (!imageUrl) return;
|
||||||
// TODO: object which is the image should have the dockData, snapAngle of 45, snapThreshold of 5
|
|
||||||
// TODO: image should be scaled down by scaleFactor
|
// Add dock to editor
|
||||||
// TODO: image should be positioned at the top left of the editor
|
fabric.Image.fromURL(
|
||||||
// TODO: image should be added to the editor
|
imageUrl,
|
||||||
// TODO: render the editor
|
function (img) {
|
||||||
}, []);
|
img.set({
|
||||||
|
left: 300, //To prevent appearing behind the sidebar
|
||||||
|
top: 0,
|
||||||
|
scaleX: scaleFactor,
|
||||||
|
scaleY: scaleFactor,
|
||||||
|
snapAngle: 45, // TODO: snapAngle
|
||||||
|
snapThreshold: 5, // TODO: snapThreshold
|
||||||
|
dockData: dockData, // TODO: attach dockData
|
||||||
|
selectable: true,
|
||||||
|
hasControls: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add to editor and make active
|
||||||
|
editor.add(img); // TODO: add to editor
|
||||||
|
editor.setActiveObject(img);
|
||||||
|
editor.requestRenderAll(); // TODO: render editor
|
||||||
|
},
|
||||||
|
{ crossOrigin: "anonymous" }
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[editor, activeDockCategory]
|
||||||
|
);
|
||||||
|
|
||||||
const getItems = useCallback((table) => {
|
const getItems = useCallback((table) => {
|
||||||
// console.log( category, materials );
|
// console.log( category, materials );
|
||||||
@@ -145,8 +169,8 @@ export const Builder = ({ editor }) => {
|
|||||||
? [
|
? [
|
||||||
...result?.model.map((item) => ({
|
...result?.model.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
type: "boatlifts"
|
type: "boatlifts",
|
||||||
}))
|
})),
|
||||||
]
|
]
|
||||||
: []
|
: []
|
||||||
);
|
);
|
||||||
@@ -158,8 +182,8 @@ export const Builder = ({ editor }) => {
|
|||||||
? [
|
? [
|
||||||
...result?.model.map((item) => ({
|
...result?.model.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
type: "accessories"
|
type: "accessories",
|
||||||
}))
|
})),
|
||||||
]
|
]
|
||||||
: []
|
: []
|
||||||
);
|
);
|
||||||
@@ -171,10 +195,10 @@ export const Builder = ({ editor }) => {
|
|||||||
? [
|
? [
|
||||||
...result?.model.map((item) => ({
|
...result?.model.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
type: "wedges"
|
type: "wedges",
|
||||||
}))
|
})),
|
||||||
]
|
]
|
||||||
: []
|
: [],
|
||||||
}));
|
}));
|
||||||
case Tables.Ramps:
|
case Tables.Ramps:
|
||||||
// return console.log( result )
|
// return console.log( result )
|
||||||
@@ -182,7 +206,7 @@ export const Builder = ({ editor }) => {
|
|||||||
...prev,
|
...prev,
|
||||||
ramps: result?.model
|
ramps: result?.model
|
||||||
? [...result?.model.map((item) => ({ ...item, type: "ramps" }))]
|
? [...result?.model.map((item) => ({ ...item, type: "ramps" }))]
|
||||||
: []
|
: [],
|
||||||
}));
|
}));
|
||||||
// return setRamps(() => [...result?.model]);
|
// return setRamps(() => [...result?.model]);
|
||||||
}
|
}
|
||||||
@@ -231,7 +255,7 @@ export const Builder = ({ editor }) => {
|
|||||||
[
|
[
|
||||||
DockPanelCategories.RollIn,
|
DockPanelCategories.RollIn,
|
||||||
DockPanelCategories.Floating,
|
DockPanelCategories.Floating,
|
||||||
DockPanelCategories.Sectional
|
DockPanelCategories.Sectional,
|
||||||
].includes(activeDockCategory)
|
].includes(activeDockCategory)
|
||||||
) {
|
) {
|
||||||
// console.log( activeDockCategory, activeMaterial )
|
// console.log( activeDockCategory, activeMaterial )
|
||||||
@@ -296,7 +320,7 @@ export const Builder = ({ editor }) => {
|
|||||||
: "bg-gray-200"
|
: "bg-gray-200"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<img className={`rounded-md`} src={GrayMaterial} alt="GreyMaterial" />
|
<img className={`rounded-md`} src={GrayMaterial} alt='GreyMaterial' />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -311,7 +335,7 @@ export const Builder = ({ editor }) => {
|
|||||||
<img
|
<img
|
||||||
className={`rounded-md`}
|
className={`rounded-md`}
|
||||||
src={PerforatedMaterial}
|
src={PerforatedMaterial}
|
||||||
alt="PerforatedMaterial"
|
alt='PerforatedMaterial'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -327,7 +351,7 @@ export const Builder = ({ editor }) => {
|
|||||||
<img
|
<img
|
||||||
className={`rounded-md`}
|
className={`rounded-md`}
|
||||||
src={WoodgrainMaterial}
|
src={WoodgrainMaterial}
|
||||||
alt="WoodgrainMaterial"
|
alt='WoodgrainMaterial'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -353,7 +377,7 @@ export const Builder = ({ editor }) => {
|
|||||||
>
|
>
|
||||||
{dock.length ? (
|
{dock.length ? (
|
||||||
<>
|
<>
|
||||||
<img src={dock[0].image} alt="" className={`rounded-md my-2`} />
|
<img src={dock[0].image} alt='' className={`rounded-md my-2`} />
|
||||||
<div className={`grid grid-cols-2`}>
|
<div className={`grid grid-cols-2`}>
|
||||||
{dock?.map((dockItem, index) => (
|
{dock?.map((dockItem, index) => (
|
||||||
<button
|
<button
|
||||||
@@ -397,7 +421,7 @@ export const Builder = ({ editor }) => {
|
|||||||
>
|
>
|
||||||
{dock.length ? (
|
{dock.length ? (
|
||||||
<>
|
<>
|
||||||
<img src={dock[0].image} alt="" className={`rounded-md my-2`} />
|
<img src={dock[0].image} alt='' className={`rounded-md my-2`} />
|
||||||
<div className={`grid grid-cols-2`}>
|
<div className={`grid grid-cols-2`}>
|
||||||
{dock?.map((dockItem, index) => (
|
{dock?.map((dockItem, index) => (
|
||||||
<button
|
<button
|
||||||
@@ -441,7 +465,7 @@ export const Builder = ({ editor }) => {
|
|||||||
>
|
>
|
||||||
{dock.length ? (
|
{dock.length ? (
|
||||||
<>
|
<>
|
||||||
<img src={dock[0].image} alt="" className={`rounded-md my-2`} />
|
<img src={dock[0].image} alt='' className={`rounded-md my-2`} />
|
||||||
<div className={`grid grid-cols-2`}>
|
<div className={`grid grid-cols-2`}>
|
||||||
{dock?.map((dockItem, index) => (
|
{dock?.map((dockItem, index) => (
|
||||||
<button
|
<button
|
||||||
@@ -484,7 +508,7 @@ export const Builder = ({ editor }) => {
|
|||||||
<>
|
<>
|
||||||
<img
|
<img
|
||||||
src={wedgesAndRamps?.wedges[0].image}
|
src={wedgesAndRamps?.wedges[0].image}
|
||||||
alt=""
|
alt=''
|
||||||
className={`rounded-md my-2`}
|
className={`rounded-md my-2`}
|
||||||
/>
|
/>
|
||||||
<div className={`grid grid-cols-2`}>
|
<div className={`grid grid-cols-2`}>
|
||||||
@@ -532,7 +556,7 @@ export const Builder = ({ editor }) => {
|
|||||||
<>
|
<>
|
||||||
<img
|
<img
|
||||||
src={wedgesAndRamps.ramps[0].image}
|
src={wedgesAndRamps.ramps[0].image}
|
||||||
alt=""
|
alt=''
|
||||||
className={`rounded-md my-2`}
|
className={`rounded-md my-2`}
|
||||||
/>
|
/>
|
||||||
<div className={`grid grid-cols-2`}>
|
<div className={`grid grid-cols-2`}>
|
||||||
@@ -606,7 +630,7 @@ export const Builder = ({ editor }) => {
|
|||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
src={boatLift[0].image}
|
src={boatLift[0].image}
|
||||||
alt=""
|
alt=''
|
||||||
className={`rounded-md my-2`}
|
className={`rounded-md my-2`}
|
||||||
/>
|
/>
|
||||||
<div className={`grid grid-cols-2`}>
|
<div className={`grid grid-cols-2`}>
|
||||||
@@ -677,7 +701,7 @@ export const Builder = ({ editor }) => {
|
|||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
src={boatLift[0].image}
|
src={boatLift[0].image}
|
||||||
alt=""
|
alt=''
|
||||||
className={`rounded-md my-2`}
|
className={`rounded-md my-2`}
|
||||||
/>
|
/>
|
||||||
<div className={`grid grid-cols-2`}>
|
<div className={`grid grid-cols-2`}>
|
||||||
@@ -735,7 +759,7 @@ export const Builder = ({ editor }) => {
|
|||||||
<span>{ dockItem.length }'</span> */}
|
<span>{ dockItem.length }'</span> */}
|
||||||
<img
|
<img
|
||||||
src={dockItem.thumbnail}
|
src={dockItem.thumbnail}
|
||||||
alt=""
|
alt=''
|
||||||
className={`rounded-md `}
|
className={`rounded-md `}
|
||||||
/>
|
/>
|
||||||
{/* </div> */}
|
{/* </div> */}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import React, {
|
|||||||
useCallback,
|
useCallback,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
useContext
|
useContext,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { FabricJSCanvas, useFabricJSEditor } from "fabricjs-react";
|
import { FabricJSCanvas, useFabricJSEditor } from "fabricjs-react";
|
||||||
import { fabric } from "fabric";
|
import { fabric } from "fabric";
|
||||||
@@ -20,7 +20,7 @@ import { clearClone, clone } from "Utils/DockBuilderUtils/clone";
|
|||||||
import {
|
import {
|
||||||
edgeDetection,
|
edgeDetection,
|
||||||
handleEdgeDetection,
|
handleEdgeDetection,
|
||||||
handleIntersection
|
handleIntersection,
|
||||||
} from "Utils/DockBuilderUtils";
|
} from "Utils/DockBuilderUtils";
|
||||||
import {
|
import {
|
||||||
CanvasModes,
|
CanvasModes,
|
||||||
@@ -32,12 +32,12 @@ import {
|
|||||||
oneFeet,
|
oneFeet,
|
||||||
rotateIcon,
|
rotateIcon,
|
||||||
scaleFactor,
|
scaleFactor,
|
||||||
twoSeventyDeg
|
twoSeventyDeg,
|
||||||
} from "Utils/constants";
|
} from "Utils/constants";
|
||||||
import {
|
import {
|
||||||
reScaleXY,
|
reScaleXY,
|
||||||
resolveHeight,
|
resolveHeight,
|
||||||
resolveWidth
|
resolveWidth,
|
||||||
} from "Utils/DockBuilderUtils/edgeDetection";
|
} from "Utils/DockBuilderUtils/edgeDetection";
|
||||||
import { DeleteIcon, RotateIcon } from "Assets/svgs";
|
import { DeleteIcon, RotateIcon } from "Assets/svgs";
|
||||||
import { capitalize } from "Utils/helper";
|
import { capitalize } from "Utils/helper";
|
||||||
@@ -90,7 +90,7 @@ export const DockBuilder = () => {
|
|||||||
const ext = "png";
|
const ext = "png";
|
||||||
const base64 = editorMemo.toDataURL({
|
const base64 = editorMemo.toDataURL({
|
||||||
format: ext,
|
format: ext,
|
||||||
enableRetinaScaling: true
|
enableRetinaScaling: true,
|
||||||
});
|
});
|
||||||
setDockImage(base64);
|
setDockImage(base64);
|
||||||
setShowEstimateModal(true);
|
setShowEstimateModal(true);
|
||||||
@@ -103,36 +103,63 @@ export const DockBuilder = () => {
|
|||||||
// }, [ editor ] );
|
// }, [ editor ] );
|
||||||
|
|
||||||
const toJSON = () => {
|
const toJSON = () => {
|
||||||
// TODO: download the json file
|
// Get JSON of editor content, including dockData and snapClone
|
||||||
// TODO: get json of editor content
|
const json = editorMemo.toJSON(["dockData", "snapClone"]);
|
||||||
// TODO: Ensure dockData is included in the json
|
const data = JSON.stringify(json, null, 2);
|
||||||
// TODO: save the json to the local storage as dock
|
// Save to localStorage
|
||||||
// TODO: name the file as paradise_dock_<timestamp here>.dock
|
localStorage.setItem("dock", data);
|
||||||
|
const blob = new Blob([data], { type: "application/json" });
|
||||||
|
const anchor = document.createElement("a");
|
||||||
|
const timestamp = Date.now();
|
||||||
|
anchor.href = URL.createObjectURL(blob);
|
||||||
|
anchor.download = `paradise_dock_${timestamp}.dock`; // TODO: name the file as paradise_dock_<timestamp here>.dock
|
||||||
|
anchor.click();
|
||||||
};
|
};
|
||||||
|
|
||||||
const uploadFile = (e) => {
|
const uploadFile = (e) => {
|
||||||
// TODO: Our own upload the file we must have downloaded previously
|
const file = e.target.files[0];
|
||||||
// TODO: extract the json from the file
|
if (!file) return;
|
||||||
// TODO: load the json to the editor
|
const reader = new FileReader();
|
||||||
// TODO: render all
|
reader.onload = function (event) {
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(event.target.result);
|
||||||
|
console.log(json);
|
||||||
|
editorMemo.loadFromJSON(json, () => {
|
||||||
|
editorMemo.renderAll();
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
alert("Invalid file format. Please select a valid .dock file.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
// Reset file input to allow reupload of the same file
|
||||||
|
e.target.value = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadImage = useCallback(() => {
|
const downloadImage = useCallback(() => {
|
||||||
// // console.log( 'Download' )
|
|
||||||
const ext = "png";
|
const ext = "png";
|
||||||
|
// Generate base64 image of the canvas
|
||||||
|
const base64 = editorMemo.toDataURL({
|
||||||
|
format: ext,
|
||||||
|
enableRetinaScaling: true,
|
||||||
|
});
|
||||||
|
// Download the image
|
||||||
|
const anchor = document.createElement("a");
|
||||||
|
const timestamp = Date.now();
|
||||||
|
anchor.href = base64;
|
||||||
|
anchor.download = `paradise_dock_snapshot_${timestamp}.${ext}`;
|
||||||
|
anchor.click();
|
||||||
|
|
||||||
// TODO: download the image
|
// Extract dockData from all objects (excluding snapClone)
|
||||||
// TODO: get the json of the editor content
|
const json = editorMemo.toJSON(["dockData", "snapClone"]);
|
||||||
// TODO: extract the dockData from the json
|
const dockDataArr = (json.objects || [])
|
||||||
// TODO: filter the dockData to ensure it does not contain snapClone
|
.filter((object) => object.dockData && !object.snapClone)
|
||||||
// TODO: generate the base64 image
|
.map((object) => object.dockData);
|
||||||
// TODO: download the image and name it as paradise_dock_snapshot_<timestamp here>.${ext}
|
|
||||||
// TODO: download the excel file of the dockData you extracted
|
|
||||||
|
|
||||||
// TODO: check if dockData is empty
|
if (dockDataArr.length > 0) {
|
||||||
// TODO: generate the base64 image
|
// Download as Excel
|
||||||
|
downloadExcel(dockDataArr);
|
||||||
// TODO: trigger download of the dockData you extracted in excel format
|
}
|
||||||
}, [editorMemo]);
|
}, [editorMemo]);
|
||||||
|
|
||||||
const renderBg = useCallback(() => {
|
const renderBg = useCallback(() => {
|
||||||
@@ -144,14 +171,14 @@ export const DockBuilder = () => {
|
|||||||
function (img) {
|
function (img) {
|
||||||
img.set({
|
img.set({
|
||||||
scaleX: editorMemo.width / img.width,
|
scaleX: editorMemo.width / img.width,
|
||||||
scaleY: editorMemo.height / img.height
|
scaleY: editorMemo.height / img.height,
|
||||||
});
|
});
|
||||||
editorMemo.setBackgroundImage(img);
|
editorMemo.setBackgroundImage(img);
|
||||||
editorMemo.renderAll();
|
editorMemo.renderAll();
|
||||||
// updateModifications( true, )
|
// updateModifications( true, )
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
crossOrigin: "anonymous"
|
crossOrigin: "anonymous",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, [editorMemo]);
|
}, [editorMemo]);
|
||||||
@@ -195,7 +222,7 @@ export const DockBuilder = () => {
|
|||||||
onChange: function (value) {
|
onChange: function (value) {
|
||||||
editorMemo.zoomToPoint({ x: pointer.x, y: pointer.y }, value);
|
editorMemo.zoomToPoint({ x: pointer.x, y: pointer.y }, value);
|
||||||
},
|
},
|
||||||
fps: 1080
|
fps: 1080,
|
||||||
});
|
});
|
||||||
opt.e.preventDefault();
|
opt.e.preventDefault();
|
||||||
opt.e.stopPropagation();
|
opt.e.stopPropagation();
|
||||||
@@ -350,12 +377,12 @@ export const DockBuilder = () => {
|
|||||||
|
|
||||||
let line = new fabric.Line([0, newTop, editorMemo.getWidth(), newTop], {
|
let line = new fabric.Line([0, newTop, editorMemo.getWidth(), newTop], {
|
||||||
stroke: "#AAAAAA",
|
stroke: "#AAAAAA",
|
||||||
testLine: true
|
testLine: true,
|
||||||
// strokeDashArray: [ 5 ],
|
// strokeDashArray: [ 5 ],
|
||||||
});
|
});
|
||||||
let line2 = new fabric.Line([0, objTop, editorMemo.getWidth(), objTop], {
|
let line2 = new fabric.Line([0, objTop, editorMemo.getWidth(), objTop], {
|
||||||
stroke: "#AAAAAA",
|
stroke: "#AAAAAA",
|
||||||
testLine: true
|
testLine: true,
|
||||||
// strokeDashArray: [ 5 ],
|
// strokeDashArray: [ 5 ],
|
||||||
});
|
});
|
||||||
editorMemo.add(line);
|
editorMemo.add(line);
|
||||||
@@ -371,12 +398,12 @@ export const DockBuilder = () => {
|
|||||||
|
|
||||||
let line = new fabric.Line([newLeft, 0, newLeft, editorMemo.getHeight()], {
|
let line = new fabric.Line([newLeft, 0, newLeft, editorMemo.getHeight()], {
|
||||||
stroke: "#AAAAAA",
|
stroke: "#AAAAAA",
|
||||||
testLine: true
|
testLine: true,
|
||||||
// strokeDashArray: [ 5 ],
|
// strokeDashArray: [ 5 ],
|
||||||
});
|
});
|
||||||
let line2 = new fabric.Line([objLeft, 0, objLeft, editorMemo.getWidth()], {
|
let line2 = new fabric.Line([objLeft, 0, objLeft, editorMemo.getWidth()], {
|
||||||
stroke: "#AAAAAA",
|
stroke: "#AAAAAA",
|
||||||
testLine: true
|
testLine: true,
|
||||||
// strokeDashArray: [ 5 ],
|
// strokeDashArray: [ 5 ],
|
||||||
});
|
});
|
||||||
editorMemo.add(line);
|
editorMemo.add(line);
|
||||||
@@ -390,9 +417,144 @@ export const DockBuilder = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Edge detection and snap to object within snap range
|
// TODO: Edge detection and snap to object within snap range
|
||||||
// TODO: Detect if the object is within the snap range of the selected object
|
if (options && options.target) {
|
||||||
// TODO: detect if the selected object contains dockData and dockData contains itemName of DockPanelCategories.Accessories || DockPanelCategories.BoatLift2 || DockPanelCategories.BoatLift4
|
const selectedObj = options.target.setCoords();
|
||||||
// TODO: handle rotation of 0, 90, 180, 270 degrees
|
|
||||||
|
// TODO: Detect if the object is within the snap range of the selected object
|
||||||
|
// This is handled by the edgeDetection function which checks if objects are within snap threshold
|
||||||
|
editorMemo.forEachObject((obj) => {
|
||||||
|
if (obj === selectedObj) return;
|
||||||
|
|
||||||
|
const detectedObjBound = obj.getBoundingRect();
|
||||||
|
const selectedObjBound = selectedObj.getBoundingRect();
|
||||||
|
|
||||||
|
// Only process image objects that are not snap clones
|
||||||
|
if (!obj.snapClone && obj.type === "image") {
|
||||||
|
// TODO: handle rotation of 0, 90, 180, 270 degrees
|
||||||
|
|
||||||
|
// Handle TOP edge snapping
|
||||||
|
if (edgeDetection(selectedObj, obj, "top")) {
|
||||||
|
let newTop = selectedObjBound.top;
|
||||||
|
|
||||||
|
// Handle different rotation combinations for top snapping
|
||||||
|
if ([0, nintyDeg].includes(obj.angle)) {
|
||||||
|
if ([oneEightyDeg, twoSeventyDeg].includes(selectedObj.angle)) {
|
||||||
|
newTop = obj.oCoords.tl.y;
|
||||||
|
} else {
|
||||||
|
newTop = obj.oCoords.tl.y - selectedObjBound.height;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ([oneEightyDeg, twoSeventyDeg].includes(selectedObj.angle)) {
|
||||||
|
newTop = obj.oCoords.tl.y - detectedObjBound.height;
|
||||||
|
} else {
|
||||||
|
newTop =
|
||||||
|
obj.oCoords.tl.y -
|
||||||
|
(selectedObjBound.height + detectedObjBound.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the new position
|
||||||
|
selectedObj.setPositionByOrigin(
|
||||||
|
{ x: selectedObj.oCoords.tl.x, y: newTop },
|
||||||
|
"left",
|
||||||
|
"top"
|
||||||
|
);
|
||||||
|
selectedObj.setCoords();
|
||||||
|
editorMemo.renderAll();
|
||||||
|
}
|
||||||
|
// Handle BOTTOM edge snapping
|
||||||
|
else if (edgeDetection(selectedObj, obj, "bottom")) {
|
||||||
|
let newTop = selectedObjBound.top;
|
||||||
|
|
||||||
|
// Handle different rotation combinations for bottom snapping
|
||||||
|
if ([0, nintyDeg].includes(obj.angle)) {
|
||||||
|
if ([oneEightyDeg, twoSeventyDeg].includes(selectedObj.angle)) {
|
||||||
|
newTop =
|
||||||
|
obj.oCoords.tl.y +
|
||||||
|
(detectedObjBound.height + selectedObjBound.height);
|
||||||
|
} else {
|
||||||
|
newTop = obj.oCoords.tl.y + detectedObjBound.height;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ([oneEightyDeg, twoSeventyDeg].includes(selectedObj.angle)) {
|
||||||
|
newTop = obj.oCoords.tl.y + selectedObjBound.height;
|
||||||
|
} else {
|
||||||
|
newTop = obj.oCoords.tl.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the new position
|
||||||
|
selectedObj.setPositionByOrigin(
|
||||||
|
{ x: selectedObj.oCoords.tl.x, y: newTop },
|
||||||
|
"left",
|
||||||
|
"top"
|
||||||
|
);
|
||||||
|
selectedObj.setCoords();
|
||||||
|
editorMemo.renderAll();
|
||||||
|
}
|
||||||
|
// Handle LEFT edge snapping
|
||||||
|
else if (edgeDetection(selectedObj, obj, "left")) {
|
||||||
|
let newLeft = selectedObjBound.left;
|
||||||
|
|
||||||
|
// Handle different rotation combinations for left snapping
|
||||||
|
if ([0, twoSeventyDeg].includes(obj.angle)) {
|
||||||
|
if ([oneEightyDeg, nintyDeg].includes(selectedObj.angle)) {
|
||||||
|
newLeft = obj.oCoords.tl.x;
|
||||||
|
} else {
|
||||||
|
newLeft = obj.oCoords.tl.x - selectedObjBound.width;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ([oneEightyDeg, nintyDeg].includes(selectedObj.angle)) {
|
||||||
|
newLeft = obj.oCoords.tl.x - detectedObjBound.width;
|
||||||
|
} else {
|
||||||
|
newLeft =
|
||||||
|
obj.oCoords.tl.x -
|
||||||
|
(selectedObjBound.width + detectedObjBound.width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the new position
|
||||||
|
selectedObj.setPositionByOrigin(
|
||||||
|
{ x: newLeft, y: selectedObj.oCoords.tl.y },
|
||||||
|
"left",
|
||||||
|
"top"
|
||||||
|
);
|
||||||
|
selectedObj.setCoords();
|
||||||
|
editorMemo.renderAll();
|
||||||
|
}
|
||||||
|
// Handle RIGHT edge snapping
|
||||||
|
else if (edgeDetection(selectedObj, obj, "right")) {
|
||||||
|
let newLeft = selectedObjBound.left;
|
||||||
|
|
||||||
|
// Handle different rotation combinations for right snapping
|
||||||
|
if ([0, twoSeventyDeg].includes(obj.angle)) {
|
||||||
|
if ([oneEightyDeg, nintyDeg].includes(selectedObj.angle)) {
|
||||||
|
newLeft =
|
||||||
|
obj.oCoords.tl.x +
|
||||||
|
(detectedObjBound.width + selectedObjBound.width);
|
||||||
|
} else {
|
||||||
|
newLeft = obj.oCoords.tl.x + detectedObjBound.width;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ([oneEightyDeg, nintyDeg].includes(selectedObj.angle)) {
|
||||||
|
newLeft = obj.oCoords.tl.x + selectedObjBound.width;
|
||||||
|
} else {
|
||||||
|
newLeft = obj.oCoords.tl.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the new position
|
||||||
|
selectedObj.setPositionByOrigin(
|
||||||
|
{ x: newLeft, y: selectedObj.oCoords.tl.y },
|
||||||
|
"left",
|
||||||
|
"top"
|
||||||
|
);
|
||||||
|
selectedObj.setCoords();
|
||||||
|
editorMemo.renderAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
editorMemo.defaultCursor = "default";
|
editorMemo.defaultCursor = "default";
|
||||||
prevPointer = null;
|
prevPointer = null;
|
||||||
@@ -497,30 +659,144 @@ export const DockBuilder = () => {
|
|||||||
}, [editor, selectedItems]);
|
}, [editor, selectedItems]);
|
||||||
|
|
||||||
const onUndoClick = useCallback(() => {
|
const onUndoClick = useCallback(() => {
|
||||||
// TODO: Undo
|
const undoResult = stack.undo();
|
||||||
|
if (undoResult.currentCount === -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (undoResult.currentCount === 0) {
|
||||||
|
editorMemo.getObjects().forEach((obj) => {
|
||||||
|
if (obj !== editorMemo.backgroundImage) {
|
||||||
|
if (obj.type == "image") {
|
||||||
|
editorMemo.remove(obj).renderAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const data = editorMemo.toJSON(["dockData"]);
|
||||||
|
localStorage.setItem("dock", JSON.stringify(data));
|
||||||
|
});
|
||||||
|
} else if (undoResult.currentCount > 0) {
|
||||||
|
const json = JSON.parse(undoResult.currentState);
|
||||||
|
editorMemo.loadFromJSON(
|
||||||
|
json,
|
||||||
|
() => {
|
||||||
|
editorMemo.renderAll();
|
||||||
|
},
|
||||||
|
(obj) => {}
|
||||||
|
);
|
||||||
|
}
|
||||||
}, [editorMemo]);
|
}, [editorMemo]);
|
||||||
|
|
||||||
const onRedoClick = useCallback(() => {
|
const onRedoClick = useCallback(() => {
|
||||||
// TODO: Redo
|
const redoResult = stack.redo();
|
||||||
|
if (!redoResult.currentState) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem("dock", redoResult.currentState);
|
||||||
|
editorMemo.loadFromJSON(
|
||||||
|
JSON.parse(redoResult.currentState),
|
||||||
|
() => {
|
||||||
|
editorMemo.renderAll();
|
||||||
|
},
|
||||||
|
(o) => {}
|
||||||
|
);
|
||||||
}, [editorMemo]);
|
}, [editorMemo]);
|
||||||
|
|
||||||
const onPrintScreen = useCallback(() => {
|
const onPrintScreen = useCallback(() => {
|
||||||
// convert the canvas to a data url and download it.
|
// Save current background
|
||||||
// TODO: Print screen
|
const originalBgColor = editorMemo.backgroundColor;
|
||||||
// TODO: print screen without background image and background color, the background image should be white
|
const originalBgImage = editorMemo.backgroundImage;
|
||||||
// TODO: after printing, the background image and background color should be restored
|
|
||||||
|
// Set background to white for printing
|
||||||
|
editorMemo.setBackgroundColor(
|
||||||
|
"#fff",
|
||||||
|
editorMemo.renderAll.bind(editorMemo)
|
||||||
|
);
|
||||||
|
editorMemo.setBackgroundImage(null, editorMemo.renderAll.bind(editorMemo));
|
||||||
|
|
||||||
|
// Give time for background to update
|
||||||
|
setTimeout(() => {
|
||||||
|
const dataUrl = editorMemo.toDataURL({
|
||||||
|
format: "png",
|
||||||
|
enableRetinaScaling: true,
|
||||||
|
});
|
||||||
|
const printWindow = window.open("", "_blank");
|
||||||
|
if (printWindow) {
|
||||||
|
printWindow.document.write(
|
||||||
|
'<html><head><title>Print Canvas</title></head><body style="margin:0"><img src="' +
|
||||||
|
dataUrl +
|
||||||
|
'" style="width:100vw;max-width:100%"/></body></html>'
|
||||||
|
);
|
||||||
|
printWindow.document.close();
|
||||||
|
printWindow.focus();
|
||||||
|
printWindow.onload = function () {
|
||||||
|
printWindow.print();
|
||||||
|
printWindow.close();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Restore original background
|
||||||
|
editorMemo.setBackgroundColor(
|
||||||
|
originalBgColor,
|
||||||
|
editorMemo.renderAll.bind(editorMemo)
|
||||||
|
);
|
||||||
|
if (originalBgImage) {
|
||||||
|
editorMemo.setBackgroundImage(
|
||||||
|
originalBgImage,
|
||||||
|
editorMemo.renderAll.bind(editorMemo)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
}, [editorMemo]);
|
}, [editorMemo]);
|
||||||
|
|
||||||
const CopySelection = () => {
|
const CopySelection = () => {
|
||||||
// TODO: Copy selection
|
const activeObject = editorMemo.getActiveObject();
|
||||||
|
if (activeObject) {
|
||||||
|
activeObject.clone(
|
||||||
|
function (cloned) {
|
||||||
|
clipboard = cloned;
|
||||||
|
},
|
||||||
|
// Ensure dockData is copied
|
||||||
|
["dockData"]
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const PasteSelection = () => {
|
const PasteSelection = () => {
|
||||||
// TODO: Paste selection
|
if (clipboard) {
|
||||||
|
clipboard.clone(
|
||||||
|
function (clonedObj) {
|
||||||
|
// Offset the pasted object so it's visible
|
||||||
|
clonedObj.set({
|
||||||
|
left: clonedObj.left + 20,
|
||||||
|
top: clonedObj.top + 20,
|
||||||
|
evented: true,
|
||||||
|
});
|
||||||
|
// Add support for multiple selected objects
|
||||||
|
if (clonedObj.type === "activeSelection") {
|
||||||
|
clonedObj.canvas = editorMemo;
|
||||||
|
clonedObj.forEachObject(function (obj) {
|
||||||
|
editorMemo.add(obj);
|
||||||
|
});
|
||||||
|
clonedObj.setCoords();
|
||||||
|
} else {
|
||||||
|
editorMemo.add(clonedObj);
|
||||||
|
}
|
||||||
|
editorMemo.setActiveObject(clonedObj);
|
||||||
|
editorMemo.requestRenderAll();
|
||||||
|
},
|
||||||
|
// Ensure dockData is copied
|
||||||
|
["dockData"]
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDeleteSelection = () => {
|
const onDeleteSelection = () => {
|
||||||
// TODO: Delete selection
|
const activeObject = editorMemo.getActiveObject();
|
||||||
|
if (activeObject) {
|
||||||
|
editorMemo.remove(activeObject);
|
||||||
|
editorMemo.discardActiveObject();
|
||||||
|
editorMemo.renderAll();
|
||||||
|
updateModifications(true, { target: activeObject });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const addLines = useCallback(() => {
|
const addLines = useCallback(() => {
|
||||||
@@ -539,7 +815,7 @@ export const DockBuilder = () => {
|
|||||||
[0, oneFeet * i, editorMemo.getWidth(), oneFeet * i],
|
[0, oneFeet * i, editorMemo.getWidth(), oneFeet * i],
|
||||||
{
|
{
|
||||||
stroke: "#AAAAAA",
|
stroke: "#AAAAAA",
|
||||||
strokeDashArray: [5]
|
strokeDashArray: [5],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -548,7 +824,7 @@ export const DockBuilder = () => {
|
|||||||
fill: "#AAAAAA",
|
fill: "#AAAAAA",
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
left: editorMemo.getWidth() - 40,
|
left: editorMemo.getWidth() - 40,
|
||||||
top: oneFeet * i
|
top: oneFeet * i,
|
||||||
});
|
});
|
||||||
|
|
||||||
multiplier++;
|
multiplier++;
|
||||||
@@ -576,7 +852,7 @@ export const DockBuilder = () => {
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "DOCK_LOADING",
|
type: "DOCK_LOADING",
|
||||||
payload: false
|
payload: false,
|
||||||
});
|
});
|
||||||
setInitialLoad(false);
|
setInitialLoad(false);
|
||||||
}, [editorMemo, showBuildCanvasFromLocalModal, dispatch, initialLoad]);
|
}, [editorMemo, showBuildCanvasFromLocalModal, dispatch, initialLoad]);
|
||||||
@@ -609,13 +885,13 @@ export const DockBuilder = () => {
|
|||||||
fabric.devicePixelRatio = 1;
|
fabric.devicePixelRatio = 1;
|
||||||
fabric.Group.prototype.hasControls = true;
|
fabric.Group.prototype.hasControls = true;
|
||||||
fabric.Group.prototype.snapAngle = 45;
|
fabric.Group.prototype.snapAngle = 45;
|
||||||
fabric.Group.prototype.snapThreshold = 5;
|
fabric.Group.prototype.snapThreshold = 10;
|
||||||
fabric.Group.prototype.stroke = "#0f75bc";
|
fabric.Group.prototype.stroke = "#0f75bc";
|
||||||
fabric.Object.prototype.snapAngle = 45;
|
fabric.Object.prototype.snapAngle = 45;
|
||||||
// Optimize object rendering
|
// Optimize object rendering
|
||||||
fabric.Object.prototype.objectCaching = true;
|
fabric.Object.prototype.objectCaching = true;
|
||||||
|
|
||||||
fabric.Object.prototype.snapThreshold = 5;
|
fabric.Object.prototype.snapThreshold = 10;
|
||||||
fabric.Object.prototype.setControlsVisibility({
|
fabric.Object.prototype.setControlsVisibility({
|
||||||
tl: false, //top-left
|
tl: false, //top-left
|
||||||
mt: false, // middle-top
|
mt: false, // middle-top
|
||||||
@@ -625,7 +901,7 @@ export const DockBuilder = () => {
|
|||||||
bl: false, // bottom-left
|
bl: false, // bottom-left
|
||||||
mb: false, //middle-bottom
|
mb: false, //middle-bottom
|
||||||
br: false, //bottom-right
|
br: false, //bottom-right
|
||||||
mtr: false // rotate icon
|
mtr: false, // rotate icon
|
||||||
});
|
});
|
||||||
// fabric.Object.prototype.controls.deleteControl = new fabric.Control( {
|
// fabric.Object.prototype.controls.deleteControl = new fabric.Control( {
|
||||||
// x: -0.8,
|
// x: -0.8,
|
||||||
@@ -654,7 +930,7 @@ export const DockBuilder = () => {
|
|||||||
sizeX: 40,
|
sizeX: 40,
|
||||||
sizeY: 40,
|
sizeY: 40,
|
||||||
touchSizeX: 40,
|
touchSizeX: 40,
|
||||||
touchSizeY: 40
|
touchSizeY: 40,
|
||||||
});
|
});
|
||||||
fabric.Object.prototype.hasControls = true;
|
fabric.Object.prototype.hasControls = true;
|
||||||
fabric.Object.prototype.lockScalingX = true;
|
fabric.Object.prototype.lockScalingX = true;
|
||||||
@@ -718,7 +994,7 @@ export const DockBuilder = () => {
|
|||||||
setInitialLoad(false);
|
setInitialLoad(false);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "DOCK_LOADING",
|
type: "DOCK_LOADING",
|
||||||
payload: false
|
payload: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -733,7 +1009,7 @@ export const DockBuilder = () => {
|
|||||||
editorMemo.setBackgroundImage(editorMemo.backgroundImage, (bgImage) => {
|
editorMemo.setBackgroundImage(editorMemo.backgroundImage, (bgImage) => {
|
||||||
bgImage.set({
|
bgImage.set({
|
||||||
scaleX: editorMemo.width / bgImage.width,
|
scaleX: editorMemo.width / bgImage.width,
|
||||||
scaleY: editorMemo.height / bgImage.height
|
scaleY: editorMemo.height / bgImage.height,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
removeLines();
|
removeLines();
|
||||||
@@ -789,8 +1065,8 @@ export const DockBuilder = () => {
|
|||||||
|
|
||||||
<img
|
<img
|
||||||
ref={imageRef}
|
ref={imageRef}
|
||||||
src=""
|
src=''
|
||||||
alt=""
|
alt=''
|
||||||
width={174}
|
width={174}
|
||||||
height={116}
|
height={116}
|
||||||
className={`rounded relative ${objHovered ? "" : "hidden"}`}
|
className={`rounded relative ${objHovered ? "" : "hidden"}`}
|
||||||
|
|||||||
Reference in New Issue
Block a user