UNPKG

merchi_product_editor

Version:

A React component for editing product images using Fabric.js

744 lines (743 loc) 39.6 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.useProductEditor = exports.ProductEditorProvider = void 0; var react_1 = __importStar(require("react")); var fabric_1 = require("fabric"); var grid_1 = require("../utils/grid"); var job_1 = require("../utils/job"); var renderUtils_1 = require("../utils/renderUtils"); var keyboard_1 = require("../utils/keyboard"); var draftTemplateUtils_1 = require("../utils/draftTemplateUtils"); var previewUtils_1 = require("../utils/previewUtils"); var lodash_1 = require("lodash"); var canvasUtils_1 = require("../utils/canvasUtils"); var fontConfig_1 = require("../config/fontConfig"); var colorConfig_1 = require("../config/colorConfig"); var uuid_1 = require("uuid"); var deviceUtils_1 = require("../utils/deviceUtils"); var ProductEditorContext = (0, react_1.createContext)(undefined); var ProductEditorProvider = function (_a) { var _b, _c; var children = _a.children, _d = _a.groupIndex, groupIndex = _d === void 0 ? 0 : _d, product = _a.product, _e = _a.width, initialWidth = _e === void 0 ? 800 : _e, // Rename prop _f = _a.height, // Rename prop initialHeight = _f === void 0 ? 600 : _f, // Rename prop job = _a.job, onSave = _a.onSave, onCancel = _a.onCancel, _g = _a.hookForm, hookForm = _g === void 0 ? null : _g, // Initialize with null _h = _a.fontOptions, // Initialize with null fontOptions = _h === void 0 ? fontConfig_1.defaultFontOptions : _h, _j = _a.colorPalette, colorPalette = _j === void 0 ? colorConfig_1.defaultPalette : _j; var inputName = "ownDrafts[0].images[".concat(groupIndex || 0, "]"); // Create refs to store the latest values to prevent excessive re-renders var allVariationsRef = (0, react_1.useRef)([]); var productRef = (0, react_1.useRef)(product); // Update product ref when it changes (0, react_1.useEffect)(function () { productRef.current = product; }, [product]); // Determine dimensions and mobile status once at initialization var deviceSettings = (0, react_1.useMemo)(function () { return (0, deviceUtils_1.getDeviceAdjustedDimensions)(initialWidth, initialHeight); }, [initialWidth, initialHeight]); // Use dimensions from device settings var canvasWidth = deviceSettings.width; var canvasHeight = deviceSettings.height; var isMobileView = deviceSettings.isMobile; var _k = (0, react_1.useState)([]), draftTemplates = _k[0], setDraftTemplates = _k[1]; var _l = (0, react_1.useState)([]), draftPreviews = _l[0], setDraftPreviews = _l[1]; var canvasRef = (0, react_1.useRef)(null); var _m = (0, react_1.useState)(null), canvas = _m[0], setCanvas = _m[1]; var _o = (0, react_1.useState)(((_c = (_b = draftTemplates === null || draftTemplates === void 0 ? void 0 : draftTemplates[0]) === null || _b === void 0 ? void 0 : _b.template) === null || _c === void 0 ? void 0 : _c.id) || null), selectedTemplate = _o[0], setSelectedTemplate = _o[1]; var _p = (0, react_1.useState)(true), showGrid = _p[0], setShowGrid = _p[1]; var _q = (0, react_1.useState)(true), showPreview = _q[0], setShowPreview = _q[1]; var _r = (0, react_1.useState)(true), isCanvasLoading = _r[0], setIsCanvasLoading = _r[1]; var _s = (0, react_1.useState)(null), selectedTextObject = _s[0], setSelectedTextObject = _s[1]; var _t = (0, react_1.useState)(false), showLayerPanel = _t[0], setShowLayerPanel = _t[1]; var _u = (0, react_1.useState)(null), selectedObjectId = _u[0], setSelectedObjectId = _u[1]; // Add a new state to track saved objects var _v = (0, react_1.useState)([]), savedObjects = _v[0], setSavedObjects = _v[1]; // Add this ref to track initial mounting var initialMountRef = (0, react_1.useRef)(false); // Initialize savedObjects from localStorage on component mount (0, react_1.useEffect)(function () { // Only reset on initial mount if (!initialMountRef.current) { initialMountRef.current = true; // Reset savedObjects on page reload instead of loading from localStorage setSavedObjects([]); } }, [product]); // Function to record the current state of variation field objects var recordVariationFieldObjectsOnCanvas = (0, react_1.useCallback)(function () { if (!canvas || selectedTemplate === null) return; // Create a fresh array instead of appending to existing var updatedSavedObjects = []; var hasChanges = false; // Get all objects from the canvas var objects = canvas.getObjects(); // Filter for objects that have a fieldId property (variation field objects) objects.forEach(function (obj) { // Get fieldId from either direct property or from variationField var fieldId = obj.fieldId; if (fieldId) { // Also save a serialized version for the savedObjects state var updatedObject = __assign({}, obj); if (obj instanceof fabric_1.fabric.Image) { Object.assign(updatedObject, { width: obj.width, height: obj.height, src: obj.getSrc(), }); } updatedSavedObjects.push(updatedObject); hasChanges = true; } }); // Only update state if we found objects with fieldIds if (hasChanges) { // Update the savedObjects state with the new complete array setSavedObjects(updatedSavedObjects); } }, [canvas, selectedTemplate]); // Remove savedObjects from the dependencies // Function to toggle preview visibility var togglePreview = function () { setShowPreview(function (prev) { return !prev; }); }; // Function to toggle layer panel visibility var toggleLayerPanel = function () { setShowLayerPanel(function (prev) { return !prev; }); }; // Function to update selected text object properties var updateSelectedText = function (props) { if (selectedTextObject && canvas && selectedTemplate !== null) { selectedTextObject.set(props); canvas.requestRenderAll(); // Record the state changes after updating text properties // This ensures color changes and other styling updates are saved setTimeout(function () { if (canvas && selectedTemplate !== null) { recordVariationFieldObjectsOnCanvas(); } }, 50); } else { console.warn('Cannot update text - missing canvas, text object, or template'); } }; var handleSave = function () { if (canvas) { var dataUrl = canvas.toDataURL(); onSave(); } }; // Handle template change with improved state tracking var handleTemplateChange = function (draftTemplate) { return __awaiter(void 0, void 0, void 0, function () { var currentCanvas, containers, i, newCanvas_1, templateData; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!canvas) return [2 /*return*/]; // Record the current state of variation field objects recordVariationFieldObjectsOnCanvas(); // Check if this is already the selected template - prevent reloading the same template if (draftTemplate.id && selectedTemplate === draftTemplate.id) { return [2 /*return*/]; } // Update the selected template if (draftTemplate.id) { setSelectedTemplate(draftTemplate.id); } currentCanvas = canvas; if (!canvasRef.current) return [3 /*break*/, 2]; // Dispose the old canvas properly try { currentCanvas.dispose(); // Remove any lingering canvas containers to prevent duplicates if (canvasRef.current.parentElement) { containers = canvasRef.current.parentElement.querySelectorAll('.canvas-container'); if (containers.length > 1) { // Keep only the first container for (i = 1; i < containers.length; i++) { containers[i].remove(); } } } } catch (e) { console.error('Error during canvas cleanup:', e); } newCanvas_1 = new fabric_1.fabric.Canvas(canvasRef.current, { width: canvasWidth, height: canvasHeight, backgroundColor: '#ffffff', // not to bring the selected object to the top visually preserveObjectStacking: true }); templateData = draftTemplates.find(function (dt) { return dt.template.id === draftTemplate.id; }); return [4 /*yield*/, (0, renderUtils_1.renderEditorOrPreview)(newCanvas_1, templateData.template, templateData.variationObjects, savedObjects, canvasHeight, canvasWidth)]; case 1: _a.sent(); // Update the state with the new canvas setCanvas(newCanvas_1); // Since selectedTemplate was just updated, we need a slight delay to ensure // grid is drawn and loading is set to false setTimeout(function () { // Draw grid if needed if (showGrid) { try { if (newCanvas_1.getElement() && newCanvas_1.getElement().parentNode) { (0, grid_1.drawGrid)(newCanvas_1, canvasWidth, canvasHeight, 20, '#a0a0a0', showGrid); } } catch (e) { console.error('Error drawing grid after template change:', e); } } setIsCanvasLoading(false); }, 50); // Re-setup keyboard events for the new canvas (0, keyboard_1.setupKeyboardEvents)(newCanvas_1, function (dataUrl) { if (document.activeElement === newCanvas_1.upperCanvasEl) { onSave && onSave(); } }); _a.label = 2; case 2: return [2 /*return*/]; } }); }); }; // Add cleanup event handlers that properly use the callback function var createEventHandler = (0, react_1.useCallback)(function (callback) { return function () { if (callback) callback(); }; }, []); // Replace the direct watch with a debounced version var debouncedWatch = (0, react_1.useMemo)(function () { return (0, lodash_1.debounce)(function (watchValues) { return __awaiter(void 0, void 0, void 0, function () { var variationsGroups, variations, newAllVariations, newDraftTemplates, currentSelectedIdStillExists, fabricCanvasInstance, templateToLoad, previouslySelected, templateData; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: // Start loading setIsCanvasLoading(true); // Record the current state of variation field objects before changes if (canvas) { recordVariationFieldObjectsOnCanvas(); } variationsGroups = watchValues.variationsGroups; variations = watchValues.variations; newAllVariations = ((_a = variationsGroups === null || variationsGroups === void 0 ? void 0 : variationsGroups[groupIndex]) === null || _a === void 0 ? void 0 : _a.variations) ? __spreadArray([], variationsGroups[groupIndex].variations, true) : variations || []; // Store in ref to avoid triggering effects allVariationsRef.current = newAllVariations; newDraftTemplates = (0, job_1.initDraftTemplates)(newAllVariations, productRef.current); currentSelectedIdStillExists = newDraftTemplates.some(function (dt) { return dt.template.id === selectedTemplate; }); fabricCanvasInstance = canvas; // Only update if they've actually changed if ((0, draftTemplateUtils_1.haveDraftTemplatesChanged)(draftTemplates, newDraftTemplates)) { setDraftTemplates(newDraftTemplates); setDraftPreviews((0, previewUtils_1.setNewDraftPreviews)(newDraftTemplates)); } templateToLoad = (_b = newDraftTemplates[0]) === null || _b === void 0 ? void 0 : _b.template; if (currentSelectedIdStillExists && selectedTemplate) { previouslySelected = newDraftTemplates.find(function (dt) { return dt.template.id === selectedTemplate; }); templateToLoad = (previouslySelected === null || previouslySelected === void 0 ? void 0 : previouslySelected.template) || templateToLoad; } setSelectedTemplate((templateToLoad === null || templateToLoad === void 0 ? void 0 : templateToLoad.id) || null); // Handle canvas setup if (fabricCanvasInstance) { // if we have a canvas we need to clear all the old templates fabricCanvasInstance.clear(); fabricCanvasInstance.setBackgroundColor('#ffffff', function () { return fabricCanvasInstance === null || fabricCanvasInstance === void 0 ? void 0 : fabricCanvasInstance.renderAll(); }); } else if (canvasRef.current) { fabricCanvasInstance = new fabric_1.fabric.Canvas(canvasRef.current, { width: canvasWidth, height: canvasHeight, backgroundColor: '#ffffff', preserveObjectStacking: true }); // Update state immediately to ensure it's available for event handlers setCanvas(fabricCanvasInstance); } else { console.error('No canvas ref available'); setIsCanvasLoading(false); return [2 /*return*/]; } if (!(templateToLoad && templateToLoad.id)) return [3 /*break*/, 2]; templateData = newDraftTemplates.find(function (dt) { return dt.template.id === templateToLoad.id; }); // Render the template onto the canvas return [4 /*yield*/, (0, renderUtils_1.renderEditorOrPreview)(fabricCanvasInstance, (templateData === null || templateData === void 0 ? void 0 : templateData.template) || {}, (templateData === null || templateData === void 0 ? void 0 : templateData.variationObjects) || [], savedObjects, canvasHeight, canvasWidth)]; case 1: // Render the template onto the canvas _c.sent(); if (fabricCanvasInstance.getElement() && fabricCanvasInstance.getElement().parentNode) { (0, grid_1.drawGrid)(fabricCanvasInstance, canvasWidth, canvasHeight, 20, '#a0a0a0', showGrid); } setIsCanvasLoading(false); return [3 /*break*/, 3]; case 2: setIsCanvasLoading(false); _c.label = 3; case 3: return [2 /*return*/]; } }); }); }, 500); }, [ groupIndex, draftTemplates, canvasRef, showGrid, canvasWidth, canvasHeight, savedObjects, canvas, selectedTemplate, recordVariationFieldObjectsOnCanvas, setSelectedTemplate ]); // Set up the debounced watch subscription (0, react_1.useEffect)(function () { if (!hookForm) return; // Subscribe to form changes var subscription = hookForm.watch(function (value) { debouncedWatch({ variationsGroups: value.variationsGroups, variations: value.variations, }); }); // Clean up subscription return function () { return subscription.unsubscribe(); }; }, [hookForm, debouncedWatch]); // This effect is used to trigger the debounced watch when the selected template is not set // This helps with things like add group. (0, react_1.useEffect)(function () { if (!selectedTemplate) { debouncedWatch({ variationsGroups: job.variationsGroups, variations: job.variations, }); } }, [groupIndex]); var _w = (0, react_1.useState)(false), loadingPreviews = _w[0], setLoadingPreviews = _w[1]; var _x = (0, react_1.useState)([]), renderedDraftPreviews = _x[0], setRenderedDraftPreviews = _x[1]; // Extract event handler setup to a separate function to avoid recreating in debounced watch var setupEventHandlers = function (fabricCanvasInstance, selectedTemplate) { if (!fabricCanvasInstance) return function () { }; // Add a flag to prevent recursive calls var isRendering = false; // Define the record changes function with canvas and template closure var recordChanges = function () { // Use the passed instance and template ID instead of state variables if (fabricCanvasInstance) { var objects = fabricCanvasInstance.getObjects(); var updatedSavedObjects_1 = []; var hasChanges_1 = false; objects.forEach(function (obj) { var fieldId = obj.fieldId; if (fieldId) { var updatedObject = __assign({}, obj); if (obj instanceof fabric_1.fabric.Image) { Object.assign(updatedObject, { width: obj.width, height: obj.height, src: obj.getSrc(), }); } updatedSavedObjects_1.push(updatedObject); hasChanges_1 = true; } }); if (hasChanges_1) { setSavedObjects(updatedSavedObjects_1); } } else { console.warn('Cannot record changes - missing canvas or templateId'); } }; // Create a bound version for event handling var boundRecordChanges = createEventHandler(recordChanges); // Add Text Toolbar Event Listener var handleSelection = function (e) { var activeObject = fabricCanvasInstance.getActiveObject(); // Check if the active object is an IText instance // Ensure the selected object has an ID if (activeObject && !activeObject.id) { activeObject.id = (0, uuid_1.v4)(); } setSelectedObjectId((activeObject === null || activeObject === void 0 ? void 0 : activeObject.id) || null); if (activeObject instanceof fabric_1.fabric.IText) { setSelectedTextObject(activeObject); } else { setSelectedTextObject(null); } }; var handleSelectionCleared = function (e) { setSelectedObjectId(null); // Clear selection in context setSelectedTextObject(null); }; var updateRenderedDraftPreviews = function () { return __awaiter(void 0, void 0, void 0, function () { var templateId, renderedImage, fullCanvasImage, activeTemplateIds, previews, existingPreviewIndex, productDraftTemplate, storedData; return __generator(this, function (_a) { switch (_a.label) { case 0: templateId = selectedTemplate; return [4 /*yield*/, (0, canvasUtils_1.renderClippedImage)(fabricCanvasInstance, { format: 'png' })]; case 1: renderedImage = _a.sent(); return [4 /*yield*/, (0, canvasUtils_1.renderCanvasWithoutGrid)(fabricCanvasInstance, { format: 'png' })]; case 2: fullCanvasImage = _a.sent(); activeTemplateIds = draftTemplates.map(function (dt) { return dt.template.id; }); if (renderedImage && templateId) { previews = __spreadArray([], renderedDraftPreviews.filter(function (rdp) { return activeTemplateIds.includes(rdp.templateId); }), true); existingPreviewIndex = previews.findIndex(function (preview) { return preview.templateId === templateId; }); if (existingPreviewIndex !== -1) { // Update existing preview previews[existingPreviewIndex] = { templateId: templateId, draft: renderedImage, canvasPreview: fullCanvasImage || '', }; } else { // Add new preview previews.push({ templateId: templateId, draft: renderedImage, canvasPreview: fullCanvasImage || '', }); } productDraftTemplate = localStorage.getItem("productDraftTemplate-".concat(product.id)); storedData = productDraftTemplate ? JSON.parse(productDraftTemplate) : []; storedData[groupIndex] = { groupIndex: groupIndex, productId: product.id, templateData: __spreadArray([], previews, true), previews: __spreadArray([], previews, true), }; localStorage.setItem("productDraftTemplate-".concat(product.id), JSON.stringify(storedData)); setRenderedDraftPreviews(previews); setLoadingPreviews(false); } return [2 /*return*/]; } }); }); }; // Add after:render event listener to call renderClippedImage var handleAfterRender = function () { return __awaiter(void 0, void 0, void 0, function () { var error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: // Prevent recursive calls if (isRendering) return [2 /*return*/]; _a.label = 1; case 1: _a.trys.push([1, 3, 4, 5]); isRendering = true; setLoadingPreviews(true); return [4 /*yield*/, updateRenderedDraftPreviews()]; case 2: _a.sent(); return [3 /*break*/, 5]; case 3: error_1 = _a.sent(); console.error('Error rendering clipped image after render:', error_1); return [3 /*break*/, 5]; case 4: isRendering = false; return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }; // Add unique ID to objects if they don't have one var ensureObjectIds = function (canvasInstance) { canvasInstance.getObjects().forEach(function (obj) { if (!obj.id) { obj.id = (0, uuid_1.v4)(); } }); }; // Ensure initial objects have IDs if (fabricCanvasInstance) { ensureObjectIds(fabricCanvasInstance); } // Attach listeners fabricCanvasInstance === null || fabricCanvasInstance === void 0 ? void 0 : fabricCanvasInstance.on('selection:created', handleSelection); fabricCanvasInstance === null || fabricCanvasInstance === void 0 ? void 0 : fabricCanvasInstance.on('selection:updated', handleSelection); fabricCanvasInstance === null || fabricCanvasInstance === void 0 ? void 0 : fabricCanvasInstance.on('selection:cleared', handleSelectionCleared); // Also update IDs when new objects are added fabricCanvasInstance === null || fabricCanvasInstance === void 0 ? void 0 : fabricCanvasInstance.on('object:added', function (e) { if (e.target && !e.target.id) { e.target.id = (0, uuid_1.v4)(); } }); // setup keyboard events var cleanupKeyboardEvents = (0, keyboard_1.setupKeyboardEvents)(fabricCanvasInstance, function () { if (document.activeElement === fabricCanvasInstance.upperCanvasEl) { onSave && onSave(); } }); fabricCanvasInstance.on('selection:created', handleSelection); fabricCanvasInstance.on('selection:updated', handleSelection); fabricCanvasInstance.on('selection:cleared', handleSelectionCleared); fabricCanvasInstance.on('after:render', handleAfterRender); // Add event listeners for object modifications fabricCanvasInstance.on('object:modified', boundRecordChanges); fabricCanvasInstance.on('object:moved', boundRecordChanges); fabricCanvasInstance.on('object:scaled', boundRecordChanges); fabricCanvasInstance.on('object:rotated', boundRecordChanges); fabricCanvasInstance.on('text:changed', boundRecordChanges); // Add handlers for more specific events that might be triggered by color changes fabricCanvasInstance.on('object:changed', boundRecordChanges); fabricCanvasInstance.on('canvas:updated', boundRecordChanges); // Return cleanup function return function () { fabricCanvasInstance.off('selection:created', handleSelection); fabricCanvasInstance.off('selection:updated', handleSelection); fabricCanvasInstance.off('selection:cleared', handleSelectionCleared); fabricCanvasInstance.off('after:render', handleAfterRender); fabricCanvasInstance.off('object:modified', boundRecordChanges); fabricCanvasInstance.off('object:moved', boundRecordChanges); fabricCanvasInstance.off('object:scaled', boundRecordChanges); fabricCanvasInstance.off('object:rotated', boundRecordChanges); fabricCanvasInstance.off('text:changed', boundRecordChanges); fabricCanvasInstance.off('object:changed', boundRecordChanges); fabricCanvasInstance.off('canvas:updated', boundRecordChanges); cleanupKeyboardEvents(); }; }; // Move setupEventHandlers call to a useEffect hook (0, react_1.useEffect)(function () { if (canvas) { try { if (canvas.getElement() && canvas.getElement().parentNode) { (0, grid_1.drawGrid)(canvas, canvasWidth, canvasHeight, 20, '#a0a0a0', showGrid); canvas.renderAll(); } } catch (error) { console.error('Error drawing/clearing grid:', error); } // Setup event handlers var cleanupEventHandlers_1 = setupEventHandlers(canvas, selectedTemplate); return function () { cleanupEventHandlers_1(); }; } }, [canvas, selectedTemplate, showGrid, canvasWidth, canvasHeight]); var selectObject = function (obj) { if (canvas) { if (obj) { if (!obj.id) { obj.id = (0, uuid_1.v4)(); } canvas.setActiveObject(obj); setSelectedObjectId(obj.id); } else { canvas.discardActiveObject(); setSelectedObjectId(null); } canvas.requestRenderAll(); } }; var deleteObject = (0, react_1.useCallback)(function (obj) { if (!canvas || !obj) return; try { setIsCanvasLoading(true); // remove the object from the canvas canvas.remove(obj); canvas.renderAll(); var fieldId_1 = obj === null || obj === void 0 ? void 0 : obj.fieldId; var fileId_1 = obj === null || obj === void 0 ? void 0 : obj.fileId; if (fieldId_1) { var currentTemplateId_1 = selectedTemplate; // update draftTemplates var updatedDraftTemplates = draftTemplates.map(function (dt) { if (dt.template.id === currentTemplateId_1) { return __assign(__assign({}, dt), { variationObjects: dt.variationObjects.filter(function (vo) { return vo.fieldId !== fieldId_1; }) }); } return dt; }); setDraftTemplates(updatedDraftTemplates); // update the form state if (hookForm) { var hasVariationsGroups = 'variationsGroups' in hookForm.getValues(); var variationsField = hasVariationsGroups ? "variationsGroups.".concat(groupIndex, ".variations") : 'variations'; var values = hookForm.getValues(variationsField) || []; var updatedValues = values.map(function (variation) { var _a, _b; if (((_a = variation === null || variation === void 0 ? void 0 : variation.variationField) === null || _a === void 0 ? void 0 : _a.id) === fieldId_1) { // file type variation and has fileId // only delete the specific file if (((_b = variation.variationFiles) === null || _b === void 0 ? void 0 : _b.length) > 0 && fileId_1) { var updatedFiles = variation.variationFiles.filter(function (file) { return file.id !== fileId_1; }); return __assign(__assign({}, variation), { value: updatedFiles.length === 0 ? null : variation.value, variationFiles: updatedFiles }); } // non file type or no fileId // clear the whole field else if (!fileId_1) { return __assign(__assign({}, variation), { value: null, variationFiles: variation.variationFiles ? [] : undefined }); } } return variation; }); hookForm.setValue(variationsField, updatedValues); } } // clear the selection state setSelectedObjectId(null); setSelectedTextObject(null); setIsCanvasLoading(false); onSave && onSave(); } catch (error) { console.error('Error in deleteObject:', error); setIsCanvasLoading(false); } }, [canvas, draftTemplates, selectedTemplate, hookForm, groupIndex, onSave]); return (react_1.default.createElement(ProductEditorContext.Provider, { value: { // States canvas: canvas, draftPreviews: draftPreviews, draftTemplates: draftTemplates, isCanvasLoading: isCanvasLoading, isMobileView: isMobileView, loadingPreviews: loadingPreviews, renderedDraftPreviews: renderedDraftPreviews, savedObjects: savedObjects, selectedTemplate: selectedTemplate, selectedTextObject: selectedTextObject, showGrid: showGrid, showPreview: showPreview, // State Setters setCanvas: setCanvas, setSelectedTemplate: setSelectedTemplate, setShowGrid: setShowGrid, // Functions handleCancel: function () { return onCancel(); }, handleSave: handleSave, handleTemplateChange: handleTemplateChange, recordVariationFieldObjectsOnCanvas: recordVariationFieldObjectsOnCanvas, togglePreview: togglePreview, updateSelectedText: updateSelectedText, fontOptions: fontOptions, colorPalette: colorPalette, showLayerPanel: showLayerPanel, toggleLayerPanel: toggleLayerPanel, selectedObjectId: selectedObjectId, setSelectedObjectId: setSelectedObjectId, selectObject: selectObject, deleteObject: deleteObject, // Props canvasRef: canvasRef, groupIndex: groupIndex, height: canvasHeight, inputName: inputName, job: job, product: product, width: canvasWidth, hookForm: hookForm, } }, children)); }; exports.ProductEditorProvider = ProductEditorProvider; var useProductEditor = function () { var context = (0, react_1.useContext)(ProductEditorContext); if (context === undefined) { throw new Error('useProductEditor must be used within a ProductEditorProvider'); } return context; }; exports.useProductEditor = useProductEditor;