merchi_product_editor
Version:
A React component for editing product images using Fabric.js
224 lines (223 loc) • 11.9 kB
JavaScript
;
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 __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 });
var react_1 = __importStar(require("react"));
var ProductEditorContext_1 = require("../context/ProductEditorContext");
var grommet_icons_1 = require("grommet-icons");
require("../styles/LayerPanel.css");
var reorder = function (list, startIndex, endIndex) {
var result = Array.from(list);
var removed = result.splice(startIndex, 1)[0];
result.splice(endIndex, 0, removed);
return result;
};
var LAYER_ITEM_HEIGHT_PX = 45;
var LayerPanel = function () {
var _a = (0, ProductEditorContext_1.useProductEditor)(), canvas = _a.canvas, toggleLayerPanel = _a.toggleLayerPanel, selectedObjectId = _a.selectedObjectId, selectObject = _a.selectObject, isMobileView = _a.isMobileView;
var _b = (0, react_1.useState)([]), layers = _b[0], setLayers = _b[1];
var dragItemIndex = (0, react_1.useRef)(null);
var dragOverItemIndex = (0, react_1.useRef)(null);
var _c = (0, react_1.useState)(false), isDraggingOver = _c[0], setIsDraggingOver = _c[1];
var _d = (0, react_1.useState)(null), dragOverIndexVisual = _d[0], setDragOverIndexVisual = _d[1];
(0, react_1.useEffect)(function () {
if (canvas) {
var updateLayers_1 = function () {
var filteredObjects = canvas.getObjects().filter(function (obj) {
return (obj.type === 'i-text' || obj.type === 'image') && obj.selectable;
});
setLayers(__spreadArray([], filteredObjects, true).reverse());
};
updateLayers_1();
var handleCanvasChange_1 = function () { return updateLayers_1(); };
canvas.on('object:added', handleCanvasChange_1);
canvas.on('object:removed', handleCanvasChange_1);
canvas.on('stack:changed', handleCanvasChange_1);
canvas.on('selection:created', handleCanvasChange_1);
canvas.on('selection:updated', handleCanvasChange_1);
canvas.on('selection:cleared', handleCanvasChange_1);
return function () {
canvas.off('object:added', handleCanvasChange_1);
canvas.off('object:removed', handleCanvasChange_1);
canvas.off('stack:changed', handleCanvasChange_1);
canvas.off('selection:created', handleCanvasChange_1);
canvas.off('selection:updated', handleCanvasChange_1);
canvas.off('selection:cleared', handleCanvasChange_1);
};
}
}, [canvas]);
var getLayerInfo = function (object) {
var _a, _b;
if (object.type === 'i-text') {
return {
name: ((_a = object.text) === null || _a === void 0 ? void 0 : _a.substring(0, 20)) || 'Text',
icon: react_1.default.createElement(grommet_icons_1.TextAlignCenter, { size: "medium" })
};
}
if (object.type === 'image') {
var imageObject = object;
var src = imageObject.getSrc();
return {
name: object.name || (src ? (_b = src.split('/').pop()) === null || _b === void 0 ? void 0 : _b.substring(0, 20) : undefined) || 'Image',
icon: react_1.default.createElement("img", { src: src, alt: "layer thumb", className: "layer-thumbnail" })
};
}
return { name: 'Unknown Layer', icon: react_1.default.createElement(react_1.default.Fragment, null) };
};
var handleDragStart = function (e, index) {
if (isMobileView)
return;
dragItemIndex.current = index;
e.dataTransfer.effectAllowed = 'move';
setTimeout(function () { return setIsDraggingOver(true); }, 0);
};
var handleDragEnterItem = function (e, index) {
if (isMobileView)
return;
e.preventDefault();
dragOverItemIndex.current = index;
setDragOverIndexVisual(index);
};
var handleDragLeaveList = function (e) {
if (isMobileView)
return;
if (!e.currentTarget.contains(e.relatedTarget)) {
dragOverItemIndex.current = null;
setDragOverIndexVisual(null);
}
};
var handleDragOverList = function (e) {
if (isMobileView)
return;
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
};
var handleDrop = function (e) {
if (isMobileView)
return;
e.preventDefault();
var sourceIndex = dragItemIndex.current;
var destinationIndex = dragOverItemIndex.current;
setIsDraggingOver(false);
setDragOverIndexVisual(null);
dragItemIndex.current = null;
dragOverItemIndex.current = null;
if (sourceIndex === null || destinationIndex === null || sourceIndex === destinationIndex) {
return;
}
var reorderedLayers = reorder(layers, sourceIndex, destinationIndex);
var objectToMove = layers[sourceIndex];
setLayers(reorderedLayers);
if (canvas && objectToMove) {
var totalObjects = canvas.getObjects().length;
var fabricTargetIndex = totalObjects - 1 - destinationIndex;
canvas.moveTo(objectToMove, fabricTargetIndex);
canvas.requestRenderAll();
}
};
var handleDragEnd = function (e) {
if (isMobileView)
return;
setIsDraggingOver(false);
setDragOverIndexVisual(null);
dragItemIndex.current = null;
dragOverItemIndex.current = null;
};
var moveLayerUp = function (index) {
if (index <= 0 || !canvas)
return;
var objectToMove = layers[index];
var objectAbove = layers[index - 1];
var fabricIndexOfAbove = canvas.getObjects().indexOf(objectAbove);
if (fabricIndexOfAbove < 0) {
console.error("Could not find objectAbove in canvas stack for moveLayerUp");
return;
}
var targetFabricIndex = fabricIndexOfAbove;
canvas.moveTo(objectToMove, targetFabricIndex);
setLayers(function (prevLayers) { return reorder(prevLayers, index, index - 1); });
canvas.requestRenderAll();
};
var moveLayerDown = function (index) {
if (index >= layers.length - 1 || !canvas)
return;
var objectToMove = layers[index];
var objectBelow = layers[index + 1];
var fabricIndexOfBelow = canvas.getObjects().indexOf(objectBelow);
if (fabricIndexOfBelow < 0) {
console.error("Could not find objectBelow in canvas stack for moveLayerDown");
return;
}
var targetFabricIndex = fabricIndexOfBelow;
canvas.moveTo(objectToMove, targetFabricIndex);
setLayers(function (prevLayers) { return reorder(prevLayers, index, index + 1); });
canvas.requestRenderAll();
};
var handleLayerClick = function (layer) {
selectObject(layer);
};
return (react_1.default.createElement("div", { className: "layer-panel" },
react_1.default.createElement("div", { className: "layer-panel-header" },
react_1.default.createElement("h3", null, "Layers"),
react_1.default.createElement("button", { onClick: toggleLayerPanel, className: "close-button", title: "Close Panel" },
react_1.default.createElement(grommet_icons_1.Close, { size: "small" }))),
react_1.default.createElement("ul", { className: "layer-list ".concat(isDraggingOver ? 'is-dragging-active' : ''), onDragOver: handleDragOverList, onDrop: handleDrop, onDragLeave: handleDragLeaveList },
layers.map(function (layer, index) {
var layerInfo = getLayerInfo(layer);
var layerId = layer.id;
var isSelected = layerId && layerId === selectedObjectId;
var isDraggingThisItem = !isMobileView && isDraggingOver && dragItemIndex.current === index;
var transformStyle = 'translateY(0px)';
if (!isMobileView && isDraggingOver && dragItemIndex.current !== null && dragOverIndexVisual !== null && !isDraggingThisItem) {
var draggingFromIndex = dragItemIndex.current;
var hoveringOverIndex = dragOverIndexVisual;
if (index > draggingFromIndex && index <= hoveringOverIndex) {
transformStyle = "translateY(-".concat(LAYER_ITEM_HEIGHT_PX, "px)");
}
else if (index < draggingFromIndex && index >= hoveringOverIndex) {
transformStyle = "translateY(".concat(LAYER_ITEM_HEIGHT_PX, "px)");
}
}
return (react_1.default.createElement("li", { key: "layer-".concat(layerId || index), draggable: !isMobileView, onDragStart: function (e) { return handleDragStart(e, index); }, onDragEnter: function (e) { return handleDragEnterItem(e, index); }, onDragEnd: handleDragEnd, onClick: function () { return handleLayerClick(layer); }, className: "layer-item ".concat(isSelected ? 'selected' : '', " ").concat(isDraggingThisItem ? 'is-dragging-item' : ''), style: { transform: transformStyle } },
react_1.default.createElement("span", { className: "layer-icon" }, layerInfo.icon),
react_1.default.createElement("span", { className: "layer-name" }, layerInfo.name),
isMobileView ? (react_1.default.createElement("div", { className: "layer-mobile-controls" },
react_1.default.createElement("button", { className: "layer-move-button", onClick: function (e) { e.stopPropagation(); moveLayerUp(index); }, disabled: index === 0, title: "Move Up" },
react_1.default.createElement(grommet_icons_1.Up, { size: "small" })),
react_1.default.createElement("button", { className: "layer-move-button", onClick: function (e) { e.stopPropagation(); moveLayerDown(index); }, disabled: index === layers.length - 1, title: "Move Down" },
react_1.default.createElement(grommet_icons_1.Down, { size: "small" })))) : (react_1.default.createElement("span", { className: "layer-drag-indicator" },
react_1.default.createElement(grommet_icons_1.Drag, { size: "small" })))));
}),
layers.length === 0 && react_1.default.createElement("li", { className: "no-layers" }, "No layers found"))));
};
exports.default = LayerPanel;