vibrant-tool 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/hooks/useKeyboardEvents.d.ts +1 -1
- package/dist/lib/services/crop-image.service.d.ts +1 -0
- package/dist/lib/types/shape.types.d.ts +1 -0
- package/dist/lib/utils/alignment-guidelines.d.ts +9 -0
- package/dist/lib/utils/background-utils.d.ts +2 -1
- package/dist/lib/utils/grid-manager.d.ts +4 -0
- package/dist/lib/utils/page-manager.d.ts +1 -0
- package/dist/modules/components/all-elements/index.d.ts +4 -1
- package/dist/modules/icons/gridicon/index.d.ts +4 -0
- package/dist/modules/toolbar/text-toolbar/index.d.ts +4 -0
- package/dist/vibrant-tool.css +149 -21
- package/dist/vibrant-tool.es.js +1686 -573
- package/dist/vibrant-tool.umd.js +1686 -573
- package/dist/zustand/editorStore.d.ts +3 -0
- package/package.json +1 -1
package/dist/vibrant-tool.es.js
CHANGED
|
@@ -1123,11 +1123,25 @@ const useEditorStore = create((set, get) => ({
|
|
|
1123
1123
|
back: null
|
|
1124
1124
|
},
|
|
1125
1125
|
templateData: null,
|
|
1126
|
+
// Grid state
|
|
1127
|
+
showGrid: false,
|
|
1128
|
+
setShowGrid: (showGrid) => set({ showGrid }),
|
|
1129
|
+
toggleGrid: () => set((state) => ({ showGrid: !state.showGrid })),
|
|
1126
1130
|
history: [],
|
|
1127
1131
|
historyIndex: -1,
|
|
1128
1132
|
// Crop state
|
|
1129
1133
|
isCropping: false,
|
|
1130
|
-
startCropping: () =>
|
|
1134
|
+
startCropping: () => {
|
|
1135
|
+
const canvas2 = get().canvas;
|
|
1136
|
+
if (canvas2 && canvas2.isDrawingMode) {
|
|
1137
|
+
canvas2.isDrawingMode = false;
|
|
1138
|
+
canvas2.selection = true;
|
|
1139
|
+
canvas2.defaultCursor = "default";
|
|
1140
|
+
canvas2._isCurrentlyDrawing = false;
|
|
1141
|
+
canvas2.requestRenderAll();
|
|
1142
|
+
}
|
|
1143
|
+
set({ isCropping: true });
|
|
1144
|
+
},
|
|
1131
1145
|
stopCropping: () => set({ isCropping: false }),
|
|
1132
1146
|
// Canvas setter (NO history here)
|
|
1133
1147
|
setCanvas: (canvas2) => {
|
|
@@ -8442,6 +8456,7 @@ function createShapeCornerOverlays(shape, bounds, pageSide = "front", overlayCol
|
|
|
8442
8456
|
originX: "left",
|
|
8443
8457
|
originY: "top",
|
|
8444
8458
|
fill: overlayColor,
|
|
8459
|
+
opacity: 0.6,
|
|
8445
8460
|
strokeWidth: 0,
|
|
8446
8461
|
selectable: false,
|
|
8447
8462
|
evented: false,
|
|
@@ -8493,6 +8508,7 @@ function createShapeCornerOverlays(shape, bounds, pageSide = "front", overlayCol
|
|
|
8493
8508
|
originX: "left",
|
|
8494
8509
|
originY: "top",
|
|
8495
8510
|
fill: overlayColor,
|
|
8511
|
+
opacity: 0.6,
|
|
8496
8512
|
strokeWidth: 0,
|
|
8497
8513
|
selectable: false,
|
|
8498
8514
|
evented: false,
|
|
@@ -8789,6 +8805,7 @@ function createOverlays(canvas2, workarea, pageSide) {
|
|
|
8789
8805
|
const overlayColor = canvas2.backgroundColor || "#F3F4F6";
|
|
8790
8806
|
const makeOverlay = (props) => new jr({
|
|
8791
8807
|
fill: overlayColor,
|
|
8808
|
+
opacity: 0.6,
|
|
8792
8809
|
selectable: false,
|
|
8793
8810
|
evented: false,
|
|
8794
8811
|
excludeFromExport: true,
|
|
@@ -8862,10 +8879,14 @@ function createOverlays(canvas2, workarea, pageSide) {
|
|
|
8862
8879
|
canvas2.requestRenderAll();
|
|
8863
8880
|
}
|
|
8864
8881
|
function bringOverlaysToFront(canvas2) {
|
|
8882
|
+
if (!canvas2) return;
|
|
8883
|
+
canvas2.preserveObjectStacking = true;
|
|
8884
|
+
canvas2.controlsAboveOverlay = true;
|
|
8865
8885
|
const overlays = canvas2.getObjects().filter((o2) => o2.name?.startsWith("overlay-"));
|
|
8866
8886
|
const guidelines = canvas2.getObjects().filter((o2) => o2.name?.startsWith("guideline-"));
|
|
8867
8887
|
overlays.forEach((overlay) => canvas2.bringObjectToFront(overlay));
|
|
8868
8888
|
guidelines.forEach((g2) => canvas2.bringObjectToFront(g2));
|
|
8889
|
+
canvas2.requestRenderAll();
|
|
8869
8890
|
}
|
|
8870
8891
|
const BASE_URL = "https://www.api.vibrantprintshop.com";
|
|
8871
8892
|
const IMAGE_UPLOAD_URL = "https://upload.vibrantprint.com";
|
|
@@ -8996,6 +9017,248 @@ const useProductStore = create((set) => ({
|
|
|
8996
9017
|
}
|
|
8997
9018
|
}
|
|
8998
9019
|
}));
|
|
9020
|
+
const FontService = {
|
|
9021
|
+
async getAll(data) {
|
|
9022
|
+
const res = await apiFetch(`/api/fonts?page=${data.page}&limit=${data.limit}&status=true`, {
|
|
9023
|
+
method: "GET"
|
|
9024
|
+
});
|
|
9025
|
+
return res;
|
|
9026
|
+
}
|
|
9027
|
+
};
|
|
9028
|
+
let cachedFontsPromise = null;
|
|
9029
|
+
async function fetchAllFontsCached() {
|
|
9030
|
+
if (!cachedFontsPromise) {
|
|
9031
|
+
cachedFontsPromise = FontService.getAll({ page: 1, limit: 1e3 }).then((res) => res?.data || []).catch((err) => {
|
|
9032
|
+
console.warn("Failed to fetch all fonts:", err);
|
|
9033
|
+
cachedFontsPromise = null;
|
|
9034
|
+
return [];
|
|
9035
|
+
});
|
|
9036
|
+
}
|
|
9037
|
+
return cachedFontsPromise;
|
|
9038
|
+
}
|
|
9039
|
+
async function injectFontFace(fontName, fontUrl) {
|
|
9040
|
+
if (!fontName || !fontUrl || typeof document === "undefined") return;
|
|
9041
|
+
const cleanedName = fontName.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9042
|
+
const id = `font-${cleanedName.replace(/\s+/g, "-").toLowerCase()}`;
|
|
9043
|
+
if (!document.getElementById(id)) {
|
|
9044
|
+
const style = document.createElement("style");
|
|
9045
|
+
style.id = id;
|
|
9046
|
+
style.innerHTML = `
|
|
9047
|
+
@font-face {
|
|
9048
|
+
font-family: '${cleanedName}';
|
|
9049
|
+
src: url('${fontUrl}');
|
|
9050
|
+
font-display: swap;
|
|
9051
|
+
}
|
|
9052
|
+
`;
|
|
9053
|
+
document.head.appendChild(style);
|
|
9054
|
+
}
|
|
9055
|
+
try {
|
|
9056
|
+
await document.fonts.load(`16px "${cleanedName}"`);
|
|
9057
|
+
await document.fonts.ready;
|
|
9058
|
+
} catch {
|
|
9059
|
+
}
|
|
9060
|
+
}
|
|
9061
|
+
async function loadTemplateFontsIntoHead(templateFonts, pagesDesignData) {
|
|
9062
|
+
if (typeof document === "undefined") return;
|
|
9063
|
+
const loadPromises = [];
|
|
9064
|
+
if (Array.isArray(templateFonts) && templateFonts.length > 0) {
|
|
9065
|
+
templateFonts.forEach((f) => {
|
|
9066
|
+
if (f && typeof f === "object") {
|
|
9067
|
+
const fontName = f.fontName || f.fontFamily;
|
|
9068
|
+
const fontUrl = f.fontFile;
|
|
9069
|
+
if (fontName && fontUrl) {
|
|
9070
|
+
loadPromises.push(injectFontFace(fontName, fontUrl));
|
|
9071
|
+
}
|
|
9072
|
+
if (f.fontFamily && f.fontFamily !== fontName && fontUrl) {
|
|
9073
|
+
loadPromises.push(injectFontFace(f.fontFamily, fontUrl));
|
|
9074
|
+
}
|
|
9075
|
+
}
|
|
9076
|
+
});
|
|
9077
|
+
}
|
|
9078
|
+
const designs = Array.isArray(pagesDesignData) ? pagesDesignData : [pagesDesignData];
|
|
9079
|
+
const usedFontNames = /* @__PURE__ */ new Set();
|
|
9080
|
+
designs.forEach((d2) => {
|
|
9081
|
+
if (d2) {
|
|
9082
|
+
const extracted = extractFontFamilies(d2);
|
|
9083
|
+
extracted.forEach((name) => usedFontNames.add(name));
|
|
9084
|
+
}
|
|
9085
|
+
});
|
|
9086
|
+
if (usedFontNames.size > 0) {
|
|
9087
|
+
try {
|
|
9088
|
+
const allFonts = await fetchAllFontsCached();
|
|
9089
|
+
usedFontNames.forEach((name) => {
|
|
9090
|
+
const lower = name.toLowerCase();
|
|
9091
|
+
const matched = allFonts.find((f) => {
|
|
9092
|
+
if (f.fontName && f.fontName.toLowerCase() === lower) return true;
|
|
9093
|
+
if (f.fontFamily && f.fontFamily.toLowerCase() === lower) return true;
|
|
9094
|
+
const children = f.childFonts || f.chieldFonts || [];
|
|
9095
|
+
return children.some(
|
|
9096
|
+
(c2) => c2.fontName && c2.fontName.toLowerCase() === lower || c2.fontFamily && c2.fontFamily.toLowerCase() === lower
|
|
9097
|
+
);
|
|
9098
|
+
});
|
|
9099
|
+
if (matched) {
|
|
9100
|
+
if (matched.fontName && matched.fontFile) {
|
|
9101
|
+
loadPromises.push(injectFontFace(matched.fontName, matched.fontFile));
|
|
9102
|
+
}
|
|
9103
|
+
if (matched.fontFamily && matched.fontFile) {
|
|
9104
|
+
loadPromises.push(injectFontFace(matched.fontFamily, matched.fontFile));
|
|
9105
|
+
}
|
|
9106
|
+
const children = matched.childFonts || matched.chieldFonts || [];
|
|
9107
|
+
children.forEach((c2) => {
|
|
9108
|
+
if (c2.fontName && c2.fontFile) {
|
|
9109
|
+
loadPromises.push(injectFontFace(c2.fontName, c2.fontFile));
|
|
9110
|
+
}
|
|
9111
|
+
});
|
|
9112
|
+
}
|
|
9113
|
+
});
|
|
9114
|
+
} catch (err) {
|
|
9115
|
+
console.warn("Failed to fetch fonts list for font injection:", err);
|
|
9116
|
+
}
|
|
9117
|
+
}
|
|
9118
|
+
await Promise.allSettled(loadPromises);
|
|
9119
|
+
try {
|
|
9120
|
+
await document.fonts.ready;
|
|
9121
|
+
} catch {
|
|
9122
|
+
}
|
|
9123
|
+
}
|
|
9124
|
+
function extractFontFamilies(designJson) {
|
|
9125
|
+
if (!designJson) return [];
|
|
9126
|
+
const fonts = /* @__PURE__ */ new Set();
|
|
9127
|
+
const traverse = (obj) => {
|
|
9128
|
+
if (!obj) return;
|
|
9129
|
+
if (obj.fontFamily && typeof obj.fontFamily === "string") {
|
|
9130
|
+
const cleaned = obj.fontFamily.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9131
|
+
if (cleaned) fonts.add(cleaned);
|
|
9132
|
+
}
|
|
9133
|
+
if (obj.fontId && typeof obj.fontId === "string") {
|
|
9134
|
+
fonts.add(obj.fontId.trim());
|
|
9135
|
+
}
|
|
9136
|
+
if (obj.styles && typeof obj.styles === "object") {
|
|
9137
|
+
Object.values(obj.styles).forEach((line) => {
|
|
9138
|
+
if (line && typeof line === "object") {
|
|
9139
|
+
Object.values(line).forEach((charStyle) => {
|
|
9140
|
+
if (charStyle?.fontFamily && typeof charStyle.fontFamily === "string") {
|
|
9141
|
+
const cleaned = charStyle.fontFamily.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9142
|
+
if (cleaned) fonts.add(cleaned);
|
|
9143
|
+
}
|
|
9144
|
+
});
|
|
9145
|
+
}
|
|
9146
|
+
});
|
|
9147
|
+
}
|
|
9148
|
+
if (Array.isArray(obj.objects)) {
|
|
9149
|
+
obj.objects.forEach(traverse);
|
|
9150
|
+
}
|
|
9151
|
+
};
|
|
9152
|
+
if (Array.isArray(designJson.objects)) {
|
|
9153
|
+
designJson.objects.forEach(traverse);
|
|
9154
|
+
} else if (Array.isArray(designJson)) {
|
|
9155
|
+
designJson.forEach(traverse);
|
|
9156
|
+
} else {
|
|
9157
|
+
traverse(designJson);
|
|
9158
|
+
}
|
|
9159
|
+
return Array.from(fonts);
|
|
9160
|
+
}
|
|
9161
|
+
async function resolveFontIds(fontNames) {
|
|
9162
|
+
if (!fontNames || !fontNames.length) return [];
|
|
9163
|
+
try {
|
|
9164
|
+
const allFonts = await fetchAllFontsCached();
|
|
9165
|
+
const matchedIds = /* @__PURE__ */ new Set();
|
|
9166
|
+
for (const rawName of fontNames) {
|
|
9167
|
+
const name = rawName.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9168
|
+
const lower = name.toLowerCase();
|
|
9169
|
+
if (/^[0-9a-fA-F]{24}$/.test(name)) {
|
|
9170
|
+
matchedIds.add(name);
|
|
9171
|
+
continue;
|
|
9172
|
+
}
|
|
9173
|
+
const matchedFont = allFonts.find((f) => {
|
|
9174
|
+
if (f._id === name) return true;
|
|
9175
|
+
if (f.fontFamily && f.fontFamily.toLowerCase() === lower) return true;
|
|
9176
|
+
if (f.fontName && f.fontName.toLowerCase() === lower) return true;
|
|
9177
|
+
const children = f.childFonts || f.chieldFonts || [];
|
|
9178
|
+
return children.some(
|
|
9179
|
+
(c2) => c2._id === name || c2.fontName && c2.fontName.toLowerCase() === lower || c2.fontFamily && c2.fontFamily.toLowerCase() === lower
|
|
9180
|
+
);
|
|
9181
|
+
});
|
|
9182
|
+
if (matchedFont?._id) {
|
|
9183
|
+
matchedIds.add(matchedFont._id);
|
|
9184
|
+
}
|
|
9185
|
+
}
|
|
9186
|
+
return Array.from(matchedIds);
|
|
9187
|
+
} catch (err) {
|
|
9188
|
+
console.warn("Failed to resolve font IDs:", err);
|
|
9189
|
+
return [];
|
|
9190
|
+
}
|
|
9191
|
+
}
|
|
9192
|
+
async function resolveFontsForExport(designs, templateFonts) {
|
|
9193
|
+
const result = [];
|
|
9194
|
+
const foundNames = /* @__PURE__ */ new Set();
|
|
9195
|
+
if (Array.isArray(templateFonts) && templateFonts.length > 0) {
|
|
9196
|
+
templateFonts.forEach((f) => {
|
|
9197
|
+
if (f && typeof f === "object") {
|
|
9198
|
+
const fontName = f.fontName || f.fontFamily;
|
|
9199
|
+
const fontFile = f.fontFile;
|
|
9200
|
+
if (fontName && fontFile) {
|
|
9201
|
+
result.push({ fontName, fontFamily: f.fontFamily, fontFile });
|
|
9202
|
+
foundNames.add(fontName.toLowerCase());
|
|
9203
|
+
if (f.fontFamily) foundNames.add(f.fontFamily.toLowerCase());
|
|
9204
|
+
}
|
|
9205
|
+
}
|
|
9206
|
+
});
|
|
9207
|
+
}
|
|
9208
|
+
const usedFontNames = /* @__PURE__ */ new Set();
|
|
9209
|
+
const designsArr = Array.isArray(designs) ? designs : [designs];
|
|
9210
|
+
designsArr.forEach((d2) => {
|
|
9211
|
+
if (d2) {
|
|
9212
|
+
const extracted = extractFontFamilies(d2);
|
|
9213
|
+
extracted.forEach((name) => {
|
|
9214
|
+
if (!foundNames.has(name.toLowerCase())) {
|
|
9215
|
+
usedFontNames.add(name);
|
|
9216
|
+
}
|
|
9217
|
+
});
|
|
9218
|
+
}
|
|
9219
|
+
});
|
|
9220
|
+
if (usedFontNames.size > 0) {
|
|
9221
|
+
try {
|
|
9222
|
+
const allFonts = await fetchAllFontsCached();
|
|
9223
|
+
usedFontNames.forEach((name) => {
|
|
9224
|
+
const lower = name.toLowerCase();
|
|
9225
|
+
const matched = allFonts.find((f) => {
|
|
9226
|
+
if (f.fontName && f.fontName.toLowerCase() === lower) return true;
|
|
9227
|
+
if (f.fontFamily && f.fontFamily.toLowerCase() === lower) return true;
|
|
9228
|
+
const children = f.childFonts || f.chieldFonts || [];
|
|
9229
|
+
return children.some(
|
|
9230
|
+
(c2) => c2.fontName && c2.fontName.toLowerCase() === lower || c2.fontFamily && c2.fontFamily.toLowerCase() === lower
|
|
9231
|
+
);
|
|
9232
|
+
});
|
|
9233
|
+
if (matched) {
|
|
9234
|
+
if (matched.fontName && matched.fontFile && !foundNames.has(matched.fontName.toLowerCase())) {
|
|
9235
|
+
result.push({
|
|
9236
|
+
fontName: matched.fontName,
|
|
9237
|
+
fontFamily: matched.fontFamily,
|
|
9238
|
+
fontFile: matched.fontFile
|
|
9239
|
+
});
|
|
9240
|
+
foundNames.add(matched.fontName.toLowerCase());
|
|
9241
|
+
}
|
|
9242
|
+
const children = matched.childFonts || matched.chieldFonts || [];
|
|
9243
|
+
children.forEach((c2) => {
|
|
9244
|
+
const childName = c2.fontName || name;
|
|
9245
|
+
if (c2.fontFile && !foundNames.has(childName.toLowerCase())) {
|
|
9246
|
+
result.push({
|
|
9247
|
+
fontName: childName,
|
|
9248
|
+
fontFamily: c2.fontFamily,
|
|
9249
|
+
fontFile: c2.fontFile
|
|
9250
|
+
});
|
|
9251
|
+
foundNames.add(childName.toLowerCase());
|
|
9252
|
+
}
|
|
9253
|
+
});
|
|
9254
|
+
}
|
|
9255
|
+
});
|
|
9256
|
+
} catch (err) {
|
|
9257
|
+
console.warn("Failed to resolve fonts for export:", err);
|
|
9258
|
+
}
|
|
9259
|
+
}
|
|
9260
|
+
return result;
|
|
9261
|
+
}
|
|
8999
9262
|
async function loadPageToCanvas(canvas2, json, pageSide) {
|
|
9000
9263
|
if (!canvas2) return;
|
|
9001
9264
|
canvas2.discardActiveObject();
|
|
@@ -9003,6 +9266,7 @@ async function loadPageToCanvas(canvas2, json, pageSide) {
|
|
|
9003
9266
|
if (json) {
|
|
9004
9267
|
try {
|
|
9005
9268
|
const parsedJson = typeof json === "string" ? JSON.parse(json) : json;
|
|
9269
|
+
await loadTemplateFontsIntoHead(null, parsedJson);
|
|
9006
9270
|
const loadPromise = canvas2.loadFromJSON(parsedJson);
|
|
9007
9271
|
const timeoutPromise = new Promise(
|
|
9008
9272
|
(_2, reject) => setTimeout(() => reject(new Error("Canvas loadFromJSON timeout")), 4e3)
|
|
@@ -9102,12 +9366,16 @@ async function loadPageToCanvas(canvas2, json, pageSide) {
|
|
|
9102
9366
|
}
|
|
9103
9367
|
}
|
|
9104
9368
|
createOverlays(canvas2, workarea, currentSide);
|
|
9369
|
+
if (bgImg) {
|
|
9370
|
+
canvas2.sendObjectToBack(bgImg);
|
|
9371
|
+
}
|
|
9105
9372
|
canvas2.sendObjectToBack(workarea);
|
|
9106
9373
|
}
|
|
9107
9374
|
canvas2.getObjects().forEach((obj) => {
|
|
9108
9375
|
const isWorkarea = obj.name === "workarea";
|
|
9109
9376
|
const isOverlayOrGuide = obj.name?.startsWith("overlay-") || obj.name?.startsWith("guideline-");
|
|
9110
|
-
|
|
9377
|
+
const isBgImage = obj.name === "bg-image";
|
|
9378
|
+
if (!isWorkarea && !isOverlayOrGuide && !isBgImage) {
|
|
9111
9379
|
obj.set({
|
|
9112
9380
|
selectable: true,
|
|
9113
9381
|
evented: true,
|
|
@@ -9245,6 +9513,77 @@ async function renderPageDataUrl(json, baseCanvas, multiplier = 1, targetSide, i
|
|
|
9245
9513
|
return "";
|
|
9246
9514
|
}
|
|
9247
9515
|
}
|
|
9516
|
+
const useDrawStore = create((set) => ({
|
|
9517
|
+
drawTool: "select",
|
|
9518
|
+
strokeWidth: 5,
|
|
9519
|
+
opacity: 100,
|
|
9520
|
+
color: "#000000",
|
|
9521
|
+
setDrawTool: (tool) => set(() => {
|
|
9522
|
+
if (tool === "highlighter") {
|
|
9523
|
+
return { drawTool: tool, opacity: 50 };
|
|
9524
|
+
}
|
|
9525
|
+
if (tool === "brush") {
|
|
9526
|
+
return { drawTool: tool, opacity: 100 };
|
|
9527
|
+
}
|
|
9528
|
+
return { drawTool: tool };
|
|
9529
|
+
}),
|
|
9530
|
+
setStrokeWidth: (v2) => set({ strokeWidth: v2 }),
|
|
9531
|
+
setOpacity: (v2) => set({ opacity: v2 }),
|
|
9532
|
+
setColor: (v2) => set({ color: v2 })
|
|
9533
|
+
}));
|
|
9534
|
+
function deriveType(objects) {
|
|
9535
|
+
if (objects.length === 0) return "none";
|
|
9536
|
+
if (objects.length > 1) return "multiple";
|
|
9537
|
+
const obj = objects[0];
|
|
9538
|
+
const t2 = obj.type?.toLowerCase();
|
|
9539
|
+
if (t2 === "activeselection") return "multiple";
|
|
9540
|
+
if (t2 === "group") {
|
|
9541
|
+
if (obj.name === "qr") return "qr";
|
|
9542
|
+
if (obj.name === "barcode") return "barcode";
|
|
9543
|
+
return "group";
|
|
9544
|
+
}
|
|
9545
|
+
if (t2 === "textbox" || t2 === "i-text" || t2 === "text") return "textbox";
|
|
9546
|
+
if (t2 === "image") return "image";
|
|
9547
|
+
return t2 || "none";
|
|
9548
|
+
}
|
|
9549
|
+
const useSelectionStore = create((set) => ({
|
|
9550
|
+
type: "none",
|
|
9551
|
+
objects: [],
|
|
9552
|
+
activeColor: null,
|
|
9553
|
+
activeColorIndex: 0,
|
|
9554
|
+
setSelection: (objects) => {
|
|
9555
|
+
if (objects && objects.length > 0) {
|
|
9556
|
+
const drawState = useDrawStore.getState();
|
|
9557
|
+
if (drawState.drawTool !== "select") {
|
|
9558
|
+
drawState.setDrawTool("select");
|
|
9559
|
+
}
|
|
9560
|
+
const canvas2 = useEditorStore.getState().canvas;
|
|
9561
|
+
if (canvas2 && canvas2.isDrawingMode) {
|
|
9562
|
+
canvas2.isDrawingMode = false;
|
|
9563
|
+
canvas2.selection = true;
|
|
9564
|
+
canvas2.defaultCursor = "default";
|
|
9565
|
+
canvas2._isCurrentlyDrawing = false;
|
|
9566
|
+
canvas2.requestRenderAll();
|
|
9567
|
+
}
|
|
9568
|
+
}
|
|
9569
|
+
set({
|
|
9570
|
+
objects,
|
|
9571
|
+
type: deriveType(objects),
|
|
9572
|
+
activeColor: null,
|
|
9573
|
+
activeColorIndex: 0
|
|
9574
|
+
});
|
|
9575
|
+
},
|
|
9576
|
+
setActiveColor: (color, index = 0) => set({
|
|
9577
|
+
activeColor: color,
|
|
9578
|
+
activeColorIndex: index
|
|
9579
|
+
}),
|
|
9580
|
+
clearSelection: () => set({
|
|
9581
|
+
type: "none",
|
|
9582
|
+
objects: [],
|
|
9583
|
+
activeColor: null,
|
|
9584
|
+
activeColorIndex: 0
|
|
9585
|
+
})
|
|
9586
|
+
}));
|
|
9248
9587
|
const HISTORY_CUSTOM_PROPERTIES = [
|
|
9249
9588
|
"name",
|
|
9250
9589
|
"id",
|
|
@@ -9255,6 +9594,9 @@ const HISTORY_CUSTOM_PROPERTIES = [
|
|
|
9255
9594
|
"lockMovementY",
|
|
9256
9595
|
"rx",
|
|
9257
9596
|
"ry",
|
|
9597
|
+
"src",
|
|
9598
|
+
"crossOrigin",
|
|
9599
|
+
"__maskPath",
|
|
9258
9600
|
"__radiusOfCanvas",
|
|
9259
9601
|
"__shapeType",
|
|
9260
9602
|
"__pageSide",
|
|
@@ -9265,7 +9607,24 @@ const HISTORY_CUSTOM_PROPERTIES = [
|
|
|
9265
9607
|
"__initialZoom",
|
|
9266
9608
|
"__borderRect",
|
|
9267
9609
|
"__rotationControlSize",
|
|
9268
|
-
"__showDegree"
|
|
9610
|
+
"__showDegree",
|
|
9611
|
+
"textAlign",
|
|
9612
|
+
"fontFamily",
|
|
9613
|
+
"fontSize",
|
|
9614
|
+
"fontWeight",
|
|
9615
|
+
"fontStyle",
|
|
9616
|
+
"underline",
|
|
9617
|
+
"linethrough",
|
|
9618
|
+
"overline",
|
|
9619
|
+
"fill",
|
|
9620
|
+
"stroke",
|
|
9621
|
+
"strokeWidth",
|
|
9622
|
+
"lineHeight",
|
|
9623
|
+
"charSpacing",
|
|
9624
|
+
"styles",
|
|
9625
|
+
"paintFirst",
|
|
9626
|
+
"strokeUniform",
|
|
9627
|
+
"objectCaching"
|
|
9269
9628
|
];
|
|
9270
9629
|
const useHistoryStore = create((set, get) => ({
|
|
9271
9630
|
canvas: null,
|
|
@@ -9288,11 +9647,12 @@ const useHistoryStore = create((set, get) => ({
|
|
|
9288
9647
|
setPage: (page) => set({ currentPage: page }),
|
|
9289
9648
|
saveState: () => {
|
|
9290
9649
|
const { canvas: canvas2, currentPage, pages, isRestoring } = get();
|
|
9291
|
-
|
|
9292
|
-
|
|
9650
|
+
const targetCanvas = canvas2 || useEditorStore.getState().canvas;
|
|
9651
|
+
if (!targetCanvas || isRestoring) return;
|
|
9652
|
+
const rawJson = targetCanvas.toObject(HISTORY_CUSTOM_PROPERTIES);
|
|
9293
9653
|
const cleanObjects = (rawJson.objects || []).filter((obj) => {
|
|
9294
9654
|
const name = obj.name || "";
|
|
9295
|
-
return name !== "watermark" && !name.startsWith("overlay-") && !name.startsWith("guideline-");
|
|
9655
|
+
return name !== "watermark" && !name.startsWith("overlay-") && !name.startsWith("guideline-") && name !== "canvas-grid";
|
|
9296
9656
|
});
|
|
9297
9657
|
const json = { ...rawJson, objects: cleanObjects };
|
|
9298
9658
|
const history = pages[currentPage]?.history ?? [];
|
|
@@ -9312,10 +9672,12 @@ const useHistoryStore = create((set, get) => ({
|
|
|
9312
9672
|
},
|
|
9313
9673
|
undo: async () => {
|
|
9314
9674
|
const { canvas: canvas2, currentPage, pages, isRestoring } = get();
|
|
9315
|
-
|
|
9675
|
+
const targetCanvas = canvas2 || useEditorStore.getState().canvas;
|
|
9676
|
+
if (!targetCanvas || isRestoring) return;
|
|
9316
9677
|
const page = pages[currentPage];
|
|
9317
9678
|
if (!page || page.history.length <= 1) return;
|
|
9318
|
-
|
|
9679
|
+
targetCanvas.discardActiveObject();
|
|
9680
|
+
useSelectionStore.getState().clearSelection();
|
|
9319
9681
|
const current = page.history.at(-1);
|
|
9320
9682
|
const previous = page.history.at(-2);
|
|
9321
9683
|
set({
|
|
@@ -9329,8 +9691,8 @@ const useHistoryStore = create((set, get) => ({
|
|
|
9329
9691
|
}
|
|
9330
9692
|
});
|
|
9331
9693
|
try {
|
|
9332
|
-
await loadPageToCanvas(
|
|
9333
|
-
|
|
9694
|
+
await loadPageToCanvas(targetCanvas, previous, currentPage);
|
|
9695
|
+
targetCanvas.requestRenderAll();
|
|
9334
9696
|
const editorStore = useEditorStore.getState();
|
|
9335
9697
|
if (editorStore.pages) {
|
|
9336
9698
|
editorStore.setPages({
|
|
@@ -9346,10 +9708,12 @@ const useHistoryStore = create((set, get) => ({
|
|
|
9346
9708
|
},
|
|
9347
9709
|
redoAction: async () => {
|
|
9348
9710
|
const { canvas: canvas2, currentPage, pages, isRestoring } = get();
|
|
9349
|
-
|
|
9711
|
+
const targetCanvas = canvas2 || useEditorStore.getState().canvas;
|
|
9712
|
+
if (!targetCanvas || isRestoring) return;
|
|
9350
9713
|
const page = pages[currentPage];
|
|
9351
9714
|
if (!page || page.redo.length === 0) return;
|
|
9352
|
-
|
|
9715
|
+
targetCanvas.discardActiveObject();
|
|
9716
|
+
useSelectionStore.getState().clearSelection();
|
|
9353
9717
|
const next = page.redo[0];
|
|
9354
9718
|
set({
|
|
9355
9719
|
isRestoring: true,
|
|
@@ -9362,8 +9726,8 @@ const useHistoryStore = create((set, get) => ({
|
|
|
9362
9726
|
}
|
|
9363
9727
|
});
|
|
9364
9728
|
try {
|
|
9365
|
-
await loadPageToCanvas(
|
|
9366
|
-
|
|
9729
|
+
await loadPageToCanvas(targetCanvas, next, currentPage);
|
|
9730
|
+
targetCanvas.requestRenderAll();
|
|
9367
9731
|
const editorStore = useEditorStore.getState();
|
|
9368
9732
|
if (editorStore.pages) {
|
|
9369
9733
|
editorStore.setPages({
|
|
@@ -9378,248 +9742,6 @@ const useHistoryStore = create((set, get) => ({
|
|
|
9378
9742
|
}
|
|
9379
9743
|
}
|
|
9380
9744
|
}));
|
|
9381
|
-
const FontService = {
|
|
9382
|
-
async getAll(data) {
|
|
9383
|
-
const res = await apiFetch(`/api/fonts?page=${data.page}&limit=${data.limit}&status=true`, {
|
|
9384
|
-
method: "GET"
|
|
9385
|
-
});
|
|
9386
|
-
return res;
|
|
9387
|
-
}
|
|
9388
|
-
};
|
|
9389
|
-
let cachedFontsPromise = null;
|
|
9390
|
-
async function fetchAllFontsCached() {
|
|
9391
|
-
if (!cachedFontsPromise) {
|
|
9392
|
-
cachedFontsPromise = FontService.getAll({ page: 1, limit: 1e3 }).then((res) => res?.data || []).catch((err) => {
|
|
9393
|
-
console.warn("Failed to fetch all fonts:", err);
|
|
9394
|
-
cachedFontsPromise = null;
|
|
9395
|
-
return [];
|
|
9396
|
-
});
|
|
9397
|
-
}
|
|
9398
|
-
return cachedFontsPromise;
|
|
9399
|
-
}
|
|
9400
|
-
async function injectFontFace(fontName, fontUrl) {
|
|
9401
|
-
if (!fontName || !fontUrl || typeof document === "undefined") return;
|
|
9402
|
-
const cleanedName = fontName.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9403
|
-
const id = `font-${cleanedName.replace(/\s+/g, "-").toLowerCase()}`;
|
|
9404
|
-
if (!document.getElementById(id)) {
|
|
9405
|
-
const style = document.createElement("style");
|
|
9406
|
-
style.id = id;
|
|
9407
|
-
style.innerHTML = `
|
|
9408
|
-
@font-face {
|
|
9409
|
-
font-family: '${cleanedName}';
|
|
9410
|
-
src: url('${fontUrl}');
|
|
9411
|
-
font-display: swap;
|
|
9412
|
-
}
|
|
9413
|
-
`;
|
|
9414
|
-
document.head.appendChild(style);
|
|
9415
|
-
}
|
|
9416
|
-
try {
|
|
9417
|
-
await document.fonts.load(`16px "${cleanedName}"`);
|
|
9418
|
-
await document.fonts.ready;
|
|
9419
|
-
} catch {
|
|
9420
|
-
}
|
|
9421
|
-
}
|
|
9422
|
-
async function loadTemplateFontsIntoHead(templateFonts, pagesDesignData) {
|
|
9423
|
-
if (typeof document === "undefined") return;
|
|
9424
|
-
const loadPromises = [];
|
|
9425
|
-
if (Array.isArray(templateFonts) && templateFonts.length > 0) {
|
|
9426
|
-
templateFonts.forEach((f) => {
|
|
9427
|
-
if (f && typeof f === "object") {
|
|
9428
|
-
const fontName = f.fontName || f.fontFamily;
|
|
9429
|
-
const fontUrl = f.fontFile;
|
|
9430
|
-
if (fontName && fontUrl) {
|
|
9431
|
-
loadPromises.push(injectFontFace(fontName, fontUrl));
|
|
9432
|
-
}
|
|
9433
|
-
if (f.fontFamily && f.fontFamily !== fontName && fontUrl) {
|
|
9434
|
-
loadPromises.push(injectFontFace(f.fontFamily, fontUrl));
|
|
9435
|
-
}
|
|
9436
|
-
}
|
|
9437
|
-
});
|
|
9438
|
-
}
|
|
9439
|
-
const designs = Array.isArray(pagesDesignData) ? pagesDesignData : [pagesDesignData];
|
|
9440
|
-
const usedFontNames = /* @__PURE__ */ new Set();
|
|
9441
|
-
designs.forEach((d2) => {
|
|
9442
|
-
if (d2) {
|
|
9443
|
-
const extracted = extractFontFamilies(d2);
|
|
9444
|
-
extracted.forEach((name) => usedFontNames.add(name));
|
|
9445
|
-
}
|
|
9446
|
-
});
|
|
9447
|
-
if (usedFontNames.size > 0) {
|
|
9448
|
-
try {
|
|
9449
|
-
const allFonts = await fetchAllFontsCached();
|
|
9450
|
-
usedFontNames.forEach((name) => {
|
|
9451
|
-
const lower = name.toLowerCase();
|
|
9452
|
-
const matched = allFonts.find((f) => {
|
|
9453
|
-
if (f.fontName && f.fontName.toLowerCase() === lower) return true;
|
|
9454
|
-
if (f.fontFamily && f.fontFamily.toLowerCase() === lower) return true;
|
|
9455
|
-
const children = f.childFonts || f.chieldFonts || [];
|
|
9456
|
-
return children.some(
|
|
9457
|
-
(c2) => c2.fontName && c2.fontName.toLowerCase() === lower || c2.fontFamily && c2.fontFamily.toLowerCase() === lower
|
|
9458
|
-
);
|
|
9459
|
-
});
|
|
9460
|
-
if (matched) {
|
|
9461
|
-
if (matched.fontName && matched.fontFile) {
|
|
9462
|
-
loadPromises.push(injectFontFace(matched.fontName, matched.fontFile));
|
|
9463
|
-
}
|
|
9464
|
-
if (matched.fontFamily && matched.fontFile) {
|
|
9465
|
-
loadPromises.push(injectFontFace(matched.fontFamily, matched.fontFile));
|
|
9466
|
-
}
|
|
9467
|
-
const children = matched.childFonts || matched.chieldFonts || [];
|
|
9468
|
-
children.forEach((c2) => {
|
|
9469
|
-
if (c2.fontName && c2.fontFile) {
|
|
9470
|
-
loadPromises.push(injectFontFace(c2.fontName, c2.fontFile));
|
|
9471
|
-
}
|
|
9472
|
-
});
|
|
9473
|
-
}
|
|
9474
|
-
});
|
|
9475
|
-
} catch (err) {
|
|
9476
|
-
console.warn("Failed to fetch fonts list for font injection:", err);
|
|
9477
|
-
}
|
|
9478
|
-
}
|
|
9479
|
-
await Promise.allSettled(loadPromises);
|
|
9480
|
-
try {
|
|
9481
|
-
await document.fonts.ready;
|
|
9482
|
-
} catch {
|
|
9483
|
-
}
|
|
9484
|
-
}
|
|
9485
|
-
function extractFontFamilies(designJson) {
|
|
9486
|
-
if (!designJson) return [];
|
|
9487
|
-
const fonts = /* @__PURE__ */ new Set();
|
|
9488
|
-
const traverse = (obj) => {
|
|
9489
|
-
if (!obj) return;
|
|
9490
|
-
if (obj.fontFamily && typeof obj.fontFamily === "string") {
|
|
9491
|
-
const cleaned = obj.fontFamily.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9492
|
-
if (cleaned) fonts.add(cleaned);
|
|
9493
|
-
}
|
|
9494
|
-
if (obj.fontId && typeof obj.fontId === "string") {
|
|
9495
|
-
fonts.add(obj.fontId.trim());
|
|
9496
|
-
}
|
|
9497
|
-
if (obj.styles && typeof obj.styles === "object") {
|
|
9498
|
-
Object.values(obj.styles).forEach((line) => {
|
|
9499
|
-
if (line && typeof line === "object") {
|
|
9500
|
-
Object.values(line).forEach((charStyle) => {
|
|
9501
|
-
if (charStyle?.fontFamily && typeof charStyle.fontFamily === "string") {
|
|
9502
|
-
const cleaned = charStyle.fontFamily.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9503
|
-
if (cleaned) fonts.add(cleaned);
|
|
9504
|
-
}
|
|
9505
|
-
});
|
|
9506
|
-
}
|
|
9507
|
-
});
|
|
9508
|
-
}
|
|
9509
|
-
if (Array.isArray(obj.objects)) {
|
|
9510
|
-
obj.objects.forEach(traverse);
|
|
9511
|
-
}
|
|
9512
|
-
};
|
|
9513
|
-
if (Array.isArray(designJson.objects)) {
|
|
9514
|
-
designJson.objects.forEach(traverse);
|
|
9515
|
-
} else if (Array.isArray(designJson)) {
|
|
9516
|
-
designJson.forEach(traverse);
|
|
9517
|
-
} else {
|
|
9518
|
-
traverse(designJson);
|
|
9519
|
-
}
|
|
9520
|
-
return Array.from(fonts);
|
|
9521
|
-
}
|
|
9522
|
-
async function resolveFontIds(fontNames) {
|
|
9523
|
-
if (!fontNames || !fontNames.length) return [];
|
|
9524
|
-
try {
|
|
9525
|
-
const allFonts = await fetchAllFontsCached();
|
|
9526
|
-
const matchedIds = /* @__PURE__ */ new Set();
|
|
9527
|
-
for (const rawName of fontNames) {
|
|
9528
|
-
const name = rawName.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9529
|
-
const lower = name.toLowerCase();
|
|
9530
|
-
if (/^[0-9a-fA-F]{24}$/.test(name)) {
|
|
9531
|
-
matchedIds.add(name);
|
|
9532
|
-
continue;
|
|
9533
|
-
}
|
|
9534
|
-
const matchedFont = allFonts.find((f) => {
|
|
9535
|
-
if (f._id === name) return true;
|
|
9536
|
-
if (f.fontFamily && f.fontFamily.toLowerCase() === lower) return true;
|
|
9537
|
-
if (f.fontName && f.fontName.toLowerCase() === lower) return true;
|
|
9538
|
-
const children = f.childFonts || f.chieldFonts || [];
|
|
9539
|
-
return children.some(
|
|
9540
|
-
(c2) => c2._id === name || c2.fontName && c2.fontName.toLowerCase() === lower || c2.fontFamily && c2.fontFamily.toLowerCase() === lower
|
|
9541
|
-
);
|
|
9542
|
-
});
|
|
9543
|
-
if (matchedFont?._id) {
|
|
9544
|
-
matchedIds.add(matchedFont._id);
|
|
9545
|
-
}
|
|
9546
|
-
}
|
|
9547
|
-
return Array.from(matchedIds);
|
|
9548
|
-
} catch (err) {
|
|
9549
|
-
console.warn("Failed to resolve font IDs:", err);
|
|
9550
|
-
return [];
|
|
9551
|
-
}
|
|
9552
|
-
}
|
|
9553
|
-
async function resolveFontsForExport(designs, templateFonts) {
|
|
9554
|
-
const result = [];
|
|
9555
|
-
const foundNames = /* @__PURE__ */ new Set();
|
|
9556
|
-
if (Array.isArray(templateFonts) && templateFonts.length > 0) {
|
|
9557
|
-
templateFonts.forEach((f) => {
|
|
9558
|
-
if (f && typeof f === "object") {
|
|
9559
|
-
const fontName = f.fontName || f.fontFamily;
|
|
9560
|
-
const fontFile = f.fontFile;
|
|
9561
|
-
if (fontName && fontFile) {
|
|
9562
|
-
result.push({ fontName, fontFamily: f.fontFamily, fontFile });
|
|
9563
|
-
foundNames.add(fontName.toLowerCase());
|
|
9564
|
-
if (f.fontFamily) foundNames.add(f.fontFamily.toLowerCase());
|
|
9565
|
-
}
|
|
9566
|
-
}
|
|
9567
|
-
});
|
|
9568
|
-
}
|
|
9569
|
-
const usedFontNames = /* @__PURE__ */ new Set();
|
|
9570
|
-
const designsArr = Array.isArray(designs) ? designs : [designs];
|
|
9571
|
-
designsArr.forEach((d2) => {
|
|
9572
|
-
if (d2) {
|
|
9573
|
-
const extracted = extractFontFamilies(d2);
|
|
9574
|
-
extracted.forEach((name) => {
|
|
9575
|
-
if (!foundNames.has(name.toLowerCase())) {
|
|
9576
|
-
usedFontNames.add(name);
|
|
9577
|
-
}
|
|
9578
|
-
});
|
|
9579
|
-
}
|
|
9580
|
-
});
|
|
9581
|
-
if (usedFontNames.size > 0) {
|
|
9582
|
-
try {
|
|
9583
|
-
const allFonts = await fetchAllFontsCached();
|
|
9584
|
-
usedFontNames.forEach((name) => {
|
|
9585
|
-
const lower = name.toLowerCase();
|
|
9586
|
-
const matched = allFonts.find((f) => {
|
|
9587
|
-
if (f.fontName && f.fontName.toLowerCase() === lower) return true;
|
|
9588
|
-
if (f.fontFamily && f.fontFamily.toLowerCase() === lower) return true;
|
|
9589
|
-
const children = f.childFonts || f.chieldFonts || [];
|
|
9590
|
-
return children.some(
|
|
9591
|
-
(c2) => c2.fontName && c2.fontName.toLowerCase() === lower || c2.fontFamily && c2.fontFamily.toLowerCase() === lower
|
|
9592
|
-
);
|
|
9593
|
-
});
|
|
9594
|
-
if (matched) {
|
|
9595
|
-
if (matched.fontName && matched.fontFile && !foundNames.has(matched.fontName.toLowerCase())) {
|
|
9596
|
-
result.push({
|
|
9597
|
-
fontName: matched.fontName,
|
|
9598
|
-
fontFamily: matched.fontFamily,
|
|
9599
|
-
fontFile: matched.fontFile
|
|
9600
|
-
});
|
|
9601
|
-
foundNames.add(matched.fontName.toLowerCase());
|
|
9602
|
-
}
|
|
9603
|
-
const children = matched.childFonts || matched.chieldFonts || [];
|
|
9604
|
-
children.forEach((c2) => {
|
|
9605
|
-
const childName = c2.fontName || name;
|
|
9606
|
-
if (c2.fontFile && !foundNames.has(childName.toLowerCase())) {
|
|
9607
|
-
result.push({
|
|
9608
|
-
fontName: childName,
|
|
9609
|
-
fontFamily: c2.fontFamily,
|
|
9610
|
-
fontFile: c2.fontFile
|
|
9611
|
-
});
|
|
9612
|
-
foundNames.add(childName.toLowerCase());
|
|
9613
|
-
}
|
|
9614
|
-
});
|
|
9615
|
-
}
|
|
9616
|
-
});
|
|
9617
|
-
} catch (err) {
|
|
9618
|
-
console.warn("Failed to resolve fonts for export:", err);
|
|
9619
|
-
}
|
|
9620
|
-
}
|
|
9621
|
-
return result;
|
|
9622
|
-
}
|
|
9623
9745
|
const WATERMARK_TEXT = "vibrantprintshop";
|
|
9624
9746
|
function createWatermark(curWorkarea, pageSide) {
|
|
9625
9747
|
const curW = curWorkarea.getScaledWidth ? curWorkarea.getScaledWidth() : curWorkarea.width * (curWorkarea.scaleX || 1);
|
|
@@ -9663,7 +9785,7 @@ function triggerDownload(url, filename) {
|
|
|
9663
9785
|
}
|
|
9664
9786
|
function exportDesignJSON(canvas2, workarea) {
|
|
9665
9787
|
const objects = canvas2.getObjects();
|
|
9666
|
-
const children = objects.filter((obj) => obj.name !== "workarea" && !obj.name?.startsWith("overlay-")).map((obj) => {
|
|
9788
|
+
const children = objects.filter((obj) => obj.name !== "workarea" && !obj.name?.startsWith("overlay-") && !obj.name?.startsWith("guideline-") && obj.name !== "canvas-grid").map((obj) => {
|
|
9667
9789
|
const base = obj.toObject();
|
|
9668
9790
|
return {
|
|
9669
9791
|
id: obj.id || crypto.randomUUID(),
|
|
@@ -9712,8 +9834,18 @@ function exportDesignJSON(canvas2, workarea) {
|
|
|
9712
9834
|
}
|
|
9713
9835
|
const ShapeService = {
|
|
9714
9836
|
async getAll(data) {
|
|
9837
|
+
const params = new URLSearchParams();
|
|
9838
|
+
if (data.page) params.append("page", String(data.page));
|
|
9839
|
+
if (data.limit) params.append("limit", String(data.limit));
|
|
9840
|
+
if (data.category && data.category !== "ALL" && data.category !== "null" && data.category !== "undefined") {
|
|
9841
|
+
params.append("category", data.category);
|
|
9842
|
+
}
|
|
9843
|
+
if (data.search && data.search.trim()) {
|
|
9844
|
+
params.append("search", data.search.trim());
|
|
9845
|
+
}
|
|
9846
|
+
params.append("status", "true");
|
|
9715
9847
|
const res = await apiFetch(
|
|
9716
|
-
`/api/cliparts
|
|
9848
|
+
`/api/cliparts?${params.toString()}`,
|
|
9717
9849
|
{
|
|
9718
9850
|
method: "GET"
|
|
9719
9851
|
}
|
|
@@ -9723,8 +9855,17 @@ const ShapeService = {
|
|
|
9723
9855
|
};
|
|
9724
9856
|
},
|
|
9725
9857
|
async getAllExternal(data) {
|
|
9858
|
+
const params = new URLSearchParams();
|
|
9859
|
+
if (data.page) params.append("page", String(data.page));
|
|
9860
|
+
if (data.limit) params.append("limit", String(data.limit));
|
|
9861
|
+
if (data.keyType) params.append("keyType", data.keyType);
|
|
9862
|
+
if (data.isVector) params.append("isVector", "true");
|
|
9863
|
+
if (data.query && data.query.trim()) {
|
|
9864
|
+
params.append("query", data.query.trim());
|
|
9865
|
+
}
|
|
9866
|
+
params.append("status", "true");
|
|
9726
9867
|
const res = await apiFetch(
|
|
9727
|
-
`/api/external/images
|
|
9868
|
+
`/api/external/images?${params.toString()}`,
|
|
9728
9869
|
{
|
|
9729
9870
|
method: "GET"
|
|
9730
9871
|
}
|
|
@@ -9888,6 +10029,7 @@ async function addImageToCanvas(url) {
|
|
|
9888
10029
|
});
|
|
9889
10030
|
canvas2.add(img);
|
|
9890
10031
|
canvas2.setActiveObject(img);
|
|
10032
|
+
bringOverlaysToFront(canvas2);
|
|
9891
10033
|
canvas2.requestRenderAll();
|
|
9892
10034
|
}).catch((err) => {
|
|
9893
10035
|
console.warn("Fabric failed to load image from URL:", url, err);
|
|
@@ -9910,7 +10052,7 @@ function captureWorkareaSVG(canvas2, workarea, pageSide) {
|
|
|
9910
10052
|
canvas2.renderAll();
|
|
9911
10053
|
const originalProps = /* @__PURE__ */ new Map();
|
|
9912
10054
|
objects.forEach((obj) => {
|
|
9913
|
-
const isExcluded = obj.excludeFromExport || obj.name?.startsWith("overlay-") || obj.name?.startsWith("guide-") || obj.name?.startsWith("guideline-") || obj.name === "crop-grid";
|
|
10055
|
+
const isExcluded = obj.excludeFromExport || obj.name?.startsWith("overlay-") || obj.name?.startsWith("guide-") || obj.name?.startsWith("guideline-") || obj.name === "canvas-grid" || obj.name === "crop-grid";
|
|
9914
10056
|
if (isExcluded && obj !== targetWorkarea) {
|
|
9915
10057
|
originalProps.set(obj, {
|
|
9916
10058
|
visible: obj.visible,
|
|
@@ -101804,8 +101946,43 @@ function TemplateMockupModal({
|
|
|
101804
101946
|
if (!uploadedImageUrl) {
|
|
101805
101947
|
throw new Error("Server upload returned empty image URL");
|
|
101806
101948
|
}
|
|
101949
|
+
const canvas22 = useEditorStore.getState().canvas;
|
|
101950
|
+
if (!templateId || !canvas22) return;
|
|
101951
|
+
const { pages: historyPages, currentPage } = useHistoryStore.getState();
|
|
101952
|
+
const editorPages = useEditorStore.getState().pages || {};
|
|
101953
|
+
const currentJson = canvas22.toObject(["name"]);
|
|
101954
|
+
const currentImage = getWorkareaDataUrl(canvas22, 1, currentPage);
|
|
101955
|
+
const pages = await Promise.all(
|
|
101956
|
+
["front", "back"].map(async (side, i) => {
|
|
101957
|
+
const isCurrent = currentPage === side;
|
|
101958
|
+
const designData = isCurrent ? currentJson : editorPages[side] || historyPages[side]?.history?.at(-1) || null;
|
|
101959
|
+
if (!designData) return null;
|
|
101960
|
+
const sideImage = isCurrent ? currentImage : await renderPageDataUrl(designData, canvas22, 1, side);
|
|
101961
|
+
const uploadedImageUrl2 = await UploadService.uploadDataUrl(
|
|
101962
|
+
sideImage,
|
|
101963
|
+
`template-${templateId}-${side}-${Date.now()}.png`
|
|
101964
|
+
);
|
|
101965
|
+
return {
|
|
101966
|
+
side,
|
|
101967
|
+
designData,
|
|
101968
|
+
image: uploadedImageUrl2,
|
|
101969
|
+
order: i + 1
|
|
101970
|
+
};
|
|
101971
|
+
})
|
|
101972
|
+
);
|
|
101973
|
+
const validPages = pages.filter(Boolean);
|
|
101974
|
+
const usedFontNames = /* @__PURE__ */ new Set();
|
|
101975
|
+
validPages.forEach((p2) => {
|
|
101976
|
+
if (p2?.designData) {
|
|
101977
|
+
const fontsInPage = extractFontFamilies(p2.designData);
|
|
101978
|
+
fontsInPage.forEach((f) => usedFontNames.add(f));
|
|
101979
|
+
}
|
|
101980
|
+
});
|
|
101981
|
+
const resolvedFontIds = await resolveFontIds(Array.from(usedFontNames));
|
|
101807
101982
|
const res = await TemplateDesignService.updateTemplateDesign(templateId, {
|
|
101808
|
-
image: uploadedImageUrl
|
|
101983
|
+
image: uploadedImageUrl,
|
|
101984
|
+
pages: validPages,
|
|
101985
|
+
fonts: resolvedFontIds
|
|
101809
101986
|
});
|
|
101810
101987
|
if (res && res.success) {
|
|
101811
101988
|
setGenerateSuccess(true);
|
|
@@ -102010,6 +102187,16 @@ function Topbar() {
|
|
|
102010
102187
|
const resetTour = useTourStore((s2) => s2.reset);
|
|
102011
102188
|
const templateId = getQueryParam("templateId") || getQueryParam("id");
|
|
102012
102189
|
const designType = (getQueryParam("designType") || getQueryParam("designtype") || getQueryParam("type"))?.toLowerCase();
|
|
102190
|
+
const handleLeavePage = () => {
|
|
102191
|
+
if (designType === "text") {
|
|
102192
|
+
spaNavigate("/design-configuration/text-templates");
|
|
102193
|
+
return;
|
|
102194
|
+
}
|
|
102195
|
+
if (designType === "template") {
|
|
102196
|
+
spaNavigate("/design-configuration/template-designs");
|
|
102197
|
+
return;
|
|
102198
|
+
}
|
|
102199
|
+
};
|
|
102013
102200
|
const handleSaveTextTemplate = async () => {
|
|
102014
102201
|
const canvas2 = useEditorStore.getState().canvas;
|
|
102015
102202
|
if (!templateId || !canvas2) return;
|
|
@@ -102104,8 +102291,42 @@ function Topbar() {
|
|
|
102104
102291
|
}
|
|
102105
102292
|
};
|
|
102106
102293
|
return /* @__PURE__ */ jsxs("div", { className: "w-full h-14 bg-[#111] text-white flex items-center justify-between px-4 border-b border-gray-800 relative z-[9999] whitespace-nowrap", children: [
|
|
102107
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-
|
|
102108
|
-
/* @__PURE__ */
|
|
102294
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 shrink-0", children: [
|
|
102295
|
+
/* @__PURE__ */ jsxs(
|
|
102296
|
+
"button",
|
|
102297
|
+
{
|
|
102298
|
+
id: "topbar-back-button",
|
|
102299
|
+
type: "button",
|
|
102300
|
+
onClick: handleLeavePage,
|
|
102301
|
+
disabled: isSaving,
|
|
102302
|
+
className: `flex items-center gap-1.5 px-2.5 py-1.5 bg-[#222] hover:bg-[#2c2c2c] active:bg-[#1a1a1a] text-gray-300 hover:text-white border border-gray-700/70 hover:border-gray-500 rounded-lg text-xs font-medium transition-all group shadow-sm ${isSaving ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}`,
|
|
102303
|
+
title: "Back / Leave page",
|
|
102304
|
+
"aria-label": "Back / Leave page",
|
|
102305
|
+
children: [
|
|
102306
|
+
/* @__PURE__ */ jsx(
|
|
102307
|
+
"svg",
|
|
102308
|
+
{
|
|
102309
|
+
className: "w-4 h-4 transition-transform duration-150 group-hover:-translate-x-0.5 text-gray-300 group-hover:text-white",
|
|
102310
|
+
fill: "none",
|
|
102311
|
+
viewBox: "0 0 24 24",
|
|
102312
|
+
stroke: "currentColor",
|
|
102313
|
+
strokeWidth: 2.2,
|
|
102314
|
+
children: /* @__PURE__ */ jsx(
|
|
102315
|
+
"path",
|
|
102316
|
+
{
|
|
102317
|
+
strokeLinecap: "round",
|
|
102318
|
+
strokeLinejoin: "round",
|
|
102319
|
+
d: "M10 19l-7-7m0 0l7-7m-7 7h18"
|
|
102320
|
+
}
|
|
102321
|
+
)
|
|
102322
|
+
}
|
|
102323
|
+
),
|
|
102324
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold tracking-wide", children: "Back" })
|
|
102325
|
+
]
|
|
102326
|
+
}
|
|
102327
|
+
),
|
|
102328
|
+
/* @__PURE__ */ jsx("div", { className: "w-px h-5 bg-gray-700/80 hidden sm:block" }),
|
|
102329
|
+
/* @__PURE__ */ jsx("h1", { className: "text-lg font-semibold tracking-tight", children: "Vibrant" }),
|
|
102109
102330
|
/* @__PURE__ */ jsx("div", { className: "w-px h-6 bg-gray-600 hidden md:block" })
|
|
102110
102331
|
] }),
|
|
102111
102332
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 shrink-0 ml-4", children: [
|
|
@@ -102390,7 +102611,23 @@ const UploadIcon = () => /* @__PURE__ */ jsx(
|
|
|
102390
102611
|
const useSidePanelStore = create(
|
|
102391
102612
|
(set) => ({
|
|
102392
102613
|
activePanel: null,
|
|
102393
|
-
setActivePanel: (val) =>
|
|
102614
|
+
setActivePanel: (val) => {
|
|
102615
|
+
if (val !== "draw") {
|
|
102616
|
+
const drawState = useDrawStore.getState();
|
|
102617
|
+
if (drawState.drawTool !== "select") {
|
|
102618
|
+
drawState.setDrawTool("select");
|
|
102619
|
+
}
|
|
102620
|
+
const canvas2 = useEditorStore.getState().canvas;
|
|
102621
|
+
if (canvas2 && canvas2.isDrawingMode) {
|
|
102622
|
+
canvas2.isDrawingMode = false;
|
|
102623
|
+
canvas2.selection = true;
|
|
102624
|
+
canvas2.defaultCursor = "default";
|
|
102625
|
+
canvas2._isCurrentlyDrawing = false;
|
|
102626
|
+
canvas2.requestRenderAll();
|
|
102627
|
+
}
|
|
102628
|
+
}
|
|
102629
|
+
set({ activePanel: val });
|
|
102630
|
+
}
|
|
102394
102631
|
})
|
|
102395
102632
|
);
|
|
102396
102633
|
function Panel({ panelName }) {
|
|
@@ -102456,43 +102693,6 @@ function Panel({ panelName }) {
|
|
|
102456
102693
|
] })
|
|
102457
102694
|
] });
|
|
102458
102695
|
}
|
|
102459
|
-
function deriveType(objects) {
|
|
102460
|
-
if (objects.length === 0) return "none";
|
|
102461
|
-
if (objects.length > 1) return "multiple";
|
|
102462
|
-
const obj = objects[0];
|
|
102463
|
-
const t2 = obj.type?.toLowerCase();
|
|
102464
|
-
if (t2 === "activeselection") return "multiple";
|
|
102465
|
-
if (t2 === "group") {
|
|
102466
|
-
if (obj.name === "qr") return "qr";
|
|
102467
|
-
if (obj.name === "barcode") return "barcode";
|
|
102468
|
-
return "group";
|
|
102469
|
-
}
|
|
102470
|
-
if (t2 === "textbox" || t2 === "i-text" || t2 === "text") return "textbox";
|
|
102471
|
-
if (t2 === "image") return "image";
|
|
102472
|
-
return t2 || "none";
|
|
102473
|
-
}
|
|
102474
|
-
const useSelectionStore = create((set) => ({
|
|
102475
|
-
type: "none",
|
|
102476
|
-
objects: [],
|
|
102477
|
-
activeColor: null,
|
|
102478
|
-
activeColorIndex: 0,
|
|
102479
|
-
setSelection: (objects) => set({
|
|
102480
|
-
objects,
|
|
102481
|
-
type: deriveType(objects),
|
|
102482
|
-
activeColor: null,
|
|
102483
|
-
activeColorIndex: 0
|
|
102484
|
-
}),
|
|
102485
|
-
setActiveColor: (color, index = 0) => set({
|
|
102486
|
-
activeColor: color,
|
|
102487
|
-
activeColorIndex: index
|
|
102488
|
-
}),
|
|
102489
|
-
clearSelection: () => set({
|
|
102490
|
-
type: "none",
|
|
102491
|
-
objects: [],
|
|
102492
|
-
activeColor: null,
|
|
102493
|
-
activeColorIndex: 0
|
|
102494
|
-
})
|
|
102495
|
-
}));
|
|
102496
102696
|
function TemplatesPanel() {
|
|
102497
102697
|
const { productData } = useProductStore();
|
|
102498
102698
|
const { startLoading, stopLoading, isLoading } = useLoaderStore();
|
|
@@ -102500,6 +102700,7 @@ function TemplatesPanel() {
|
|
|
102500
102700
|
const [page, setPage] = useState(1);
|
|
102501
102701
|
const scrollRef = useRef(null);
|
|
102502
102702
|
const [hasMore, setHasMore] = useState(true);
|
|
102703
|
+
const templateId = getQueryParam("templateId");
|
|
102503
102704
|
useEffect(() => {
|
|
102504
102705
|
if (!productData?._id) return;
|
|
102505
102706
|
startLoading();
|
|
@@ -102591,7 +102792,7 @@ function TemplatesPanel() {
|
|
|
102591
102792
|
)
|
|
102592
102793
|
] }),
|
|
102593
102794
|
/* @__PURE__ */ jsx("div", { ref: scrollRef, className: "h-[calc(100vh-125px)] overflow-auto custom-scrollbar", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3 pb-12 mx-4", children: [
|
|
102594
|
-
templates.map((tpl) => /* @__PURE__ */ jsx(
|
|
102795
|
+
templates.filter((t2) => t2.image && t2._id !== templateId).map((tpl) => /* @__PURE__ */ jsx(
|
|
102595
102796
|
"button",
|
|
102596
102797
|
{
|
|
102597
102798
|
className: "rounded overflow-hidden group relative cursor-pointer border border-transparent hover:border-[#02bc71] transition-all",
|
|
@@ -102933,6 +103134,7 @@ async function addSVGToCanvas(type = "svg", url) {
|
|
|
102933
103134
|
svg.setCoords();
|
|
102934
103135
|
canvas2.add(svg);
|
|
102935
103136
|
canvas2.setActiveObject(svg);
|
|
103137
|
+
bringOverlaysToFront(canvas2);
|
|
102936
103138
|
useSelectionStore.getState().setSelection([svg]);
|
|
102937
103139
|
canvas2.requestRenderAll();
|
|
102938
103140
|
}
|
|
@@ -102983,6 +103185,7 @@ async function renderSVGStringToCanvas(type = "svg", svg, options = {}) {
|
|
|
102983
103185
|
group.setCoords();
|
|
102984
103186
|
canvas2.add(group);
|
|
102985
103187
|
canvas2.setActiveObject(group);
|
|
103188
|
+
bringOverlaysToFront(canvas2);
|
|
102986
103189
|
useSelectionStore.getState().setSelection([group]);
|
|
102987
103190
|
canvas2.requestRenderAll();
|
|
102988
103191
|
return group;
|
|
@@ -103052,26 +103255,63 @@ function AllElements({
|
|
|
103052
103255
|
elements: elements2,
|
|
103053
103256
|
onBack,
|
|
103054
103257
|
scrollRef,
|
|
103055
|
-
onSearch
|
|
103258
|
+
onSearch,
|
|
103259
|
+
initialSearch = "",
|
|
103260
|
+
title,
|
|
103261
|
+
backText
|
|
103056
103262
|
}) {
|
|
103057
|
-
const [search, setSearch] = useState(
|
|
103058
|
-
const { isLoading } = useLoaderStore
|
|
103263
|
+
const [search, setSearch] = useState(initialSearch);
|
|
103264
|
+
const { isLoading } = useLoaderStore();
|
|
103059
103265
|
const { addRecentItem } = useRecentElementsStore();
|
|
103266
|
+
useEffect(() => {
|
|
103267
|
+
setSearch(initialSearch);
|
|
103268
|
+
}, [initialSearch]);
|
|
103060
103269
|
const clearSearch = () => {
|
|
103061
103270
|
setSearch("");
|
|
103062
103271
|
onSearch("");
|
|
103063
103272
|
};
|
|
103064
|
-
const
|
|
103065
|
-
(
|
|
103066
|
-
|
|
103067
|
-
|
|
103068
|
-
|
|
103069
|
-
|
|
103070
|
-
|
|
103071
|
-
|
|
103072
|
-
|
|
103073
|
-
|
|
103074
|
-
|
|
103273
|
+
const handleSearchSubmit = () => {
|
|
103274
|
+
onSearch(search.trim());
|
|
103275
|
+
};
|
|
103276
|
+
const handleKeyDown = (e3) => {
|
|
103277
|
+
e3.stopPropagation();
|
|
103278
|
+
if (e3.key === "Enter") {
|
|
103279
|
+
handleSearchSubmit();
|
|
103280
|
+
}
|
|
103281
|
+
};
|
|
103282
|
+
const dynamicBackLabel = backText || (title ? title : "Back");
|
|
103283
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
103284
|
+
/* @__PURE__ */ jsx("div", { className: "mx-4 mb-2.5 flex items-center", children: /* @__PURE__ */ jsxs(
|
|
103285
|
+
"button",
|
|
103286
|
+
{
|
|
103287
|
+
type: "button",
|
|
103288
|
+
onClick: onBack,
|
|
103289
|
+
className: "cursor-pointer flex items-center gap-1.5 text-xs font-semibold text-neutral-300 hover:text-white transition-colors group",
|
|
103290
|
+
title: `Back to ${dynamicBackLabel}`,
|
|
103291
|
+
children: [
|
|
103292
|
+
/* @__PURE__ */ jsxs(
|
|
103293
|
+
"svg",
|
|
103294
|
+
{
|
|
103295
|
+
width: "16",
|
|
103296
|
+
height: "16",
|
|
103297
|
+
viewBox: "0 0 24 24",
|
|
103298
|
+
fill: "none",
|
|
103299
|
+
stroke: "currentColor",
|
|
103300
|
+
strokeWidth: "2.5",
|
|
103301
|
+
strokeLinecap: "round",
|
|
103302
|
+
strokeLinejoin: "round",
|
|
103303
|
+
className: "group-hover:-translate-x-0.5 transition-transform text-neutral-400 group-hover:text-white",
|
|
103304
|
+
children: [
|
|
103305
|
+
/* @__PURE__ */ jsx("line", { x1: "19", y1: "12", x2: "5", y2: "12" }),
|
|
103306
|
+
/* @__PURE__ */ jsx("polyline", { points: "12 19 5 12 12 5" })
|
|
103307
|
+
]
|
|
103308
|
+
}
|
|
103309
|
+
),
|
|
103310
|
+
/* @__PURE__ */ jsx("span", { className: "capitalize truncate max-w-[240px]", children: dynamicBackLabel })
|
|
103311
|
+
]
|
|
103312
|
+
}
|
|
103313
|
+
) }),
|
|
103314
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-neutral-800 px-3 py-2 rounded flex items-center gap-2 mb-4 mx-4 relative focus-within:ring-1 focus-within:ring-neutral-600", children: [
|
|
103075
103315
|
/* @__PURE__ */ jsxs(
|
|
103076
103316
|
"svg",
|
|
103077
103317
|
{
|
|
@@ -103079,7 +103319,8 @@ function AllElements({
|
|
|
103079
103319
|
stroke: "currentColor",
|
|
103080
103320
|
fill: "none",
|
|
103081
103321
|
viewBox: "0 0 24 24",
|
|
103082
|
-
className: "text-gray-
|
|
103322
|
+
className: "text-gray-400 shrink-0 cursor-pointer hover:text-white transition-colors",
|
|
103323
|
+
onClick: handleSearchSubmit,
|
|
103083
103324
|
children: [
|
|
103084
103325
|
/* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
|
|
103085
103326
|
/* @__PURE__ */ jsx("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })
|
|
@@ -103091,9 +103332,10 @@ function AllElements({
|
|
|
103091
103332
|
{
|
|
103092
103333
|
type: "text",
|
|
103093
103334
|
value: search,
|
|
103094
|
-
onChange:
|
|
103095
|
-
onKeyDown:
|
|
103096
|
-
|
|
103335
|
+
onChange: (e3) => setSearch(e3.target.value),
|
|
103336
|
+
onKeyDown: handleKeyDown,
|
|
103337
|
+
placeholder: title ? `Search in ${title}...` : "Search elements & shapes...",
|
|
103338
|
+
className: "bg-transparent outline-none text-sm text-gray-200 placeholder-gray-500 w-full pr-8"
|
|
103097
103339
|
}
|
|
103098
103340
|
),
|
|
103099
103341
|
search && /* @__PURE__ */ jsx(
|
|
@@ -103101,13 +103343,13 @@ function AllElements({
|
|
|
103101
103343
|
{
|
|
103102
103344
|
type: "button",
|
|
103103
103345
|
onClick: clearSearch,
|
|
103104
|
-
className: "absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-white transition-colors p-1",
|
|
103346
|
+
className: "absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-white transition-colors p-1 cursor-pointer",
|
|
103105
103347
|
"aria-label": "Clear search",
|
|
103106
103348
|
children: /* @__PURE__ */ jsxs(
|
|
103107
103349
|
"svg",
|
|
103108
103350
|
{
|
|
103109
|
-
width: "
|
|
103110
|
-
height: "
|
|
103351
|
+
width: "14",
|
|
103352
|
+
height: "14",
|
|
103111
103353
|
viewBox: "0 0 24 24",
|
|
103112
103354
|
fill: "none",
|
|
103113
103355
|
stroke: "currentColor",
|
|
@@ -103121,9 +103363,20 @@ function AllElements({
|
|
|
103121
103363
|
}
|
|
103122
103364
|
)
|
|
103123
103365
|
] }),
|
|
103124
|
-
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px]" }),
|
|
103125
|
-
/* @__PURE__ */ jsx("div", { className: "h-
|
|
103126
|
-
/* @__PURE__ */ jsx("
|
|
103366
|
+
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px] border-neutral-800" }),
|
|
103367
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-auto custom-scrollbar", ref: scrollRef, children: /* @__PURE__ */ jsx("div", { className: "mx-4 py-5", children: elements2.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "py-12 text-center", children: [
|
|
103368
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-400 text-sm", children: search.trim() ? `No results found for "${search}"` : "No elements found" }),
|
|
103369
|
+
search.trim() && /* @__PURE__ */ jsx(
|
|
103370
|
+
"button",
|
|
103371
|
+
{
|
|
103372
|
+
type: "button",
|
|
103373
|
+
onClick: clearSearch,
|
|
103374
|
+
className: "mt-3 text-xs text-emerald-400 hover:underline cursor-pointer",
|
|
103375
|
+
children: "Clear search"
|
|
103376
|
+
}
|
|
103377
|
+
)
|
|
103378
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
103379
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 md:grid-cols-3 gap-3", children: elements2.map((shape, i) => /* @__PURE__ */ jsx(
|
|
103127
103380
|
"button",
|
|
103128
103381
|
{
|
|
103129
103382
|
type: "button",
|
|
@@ -103138,21 +103391,22 @@ function AllElements({
|
|
|
103138
103391
|
useSidePanelStore.getState().setActivePanel(null);
|
|
103139
103392
|
}
|
|
103140
103393
|
},
|
|
103141
|
-
className: "w-full cursor-pointer flex items-center justify-center",
|
|
103394
|
+
className: "w-full cursor-pointer flex items-center justify-center p-2 rounded-lg bg-neutral-900/40 hover:bg-neutral-800 border border-neutral-800/60 hover:border-neutral-700 transition-colors",
|
|
103142
103395
|
children: shape.image && /* @__PURE__ */ jsx(
|
|
103143
103396
|
"img",
|
|
103144
103397
|
{
|
|
103145
103398
|
src: shape.image,
|
|
103146
|
-
alt: shape.category,
|
|
103399
|
+
alt: shape.category || "element",
|
|
103147
103400
|
width: 150,
|
|
103148
103401
|
height: 150,
|
|
103149
|
-
className: "w-full h-
|
|
103402
|
+
className: "w-full h-20 object-contain",
|
|
103403
|
+
loading: "lazy"
|
|
103150
103404
|
}
|
|
103151
103405
|
)
|
|
103152
103406
|
},
|
|
103153
|
-
i
|
|
103407
|
+
shape._id || shape.image || i
|
|
103154
103408
|
)) }),
|
|
103155
|
-
isLoading && /* @__PURE__ */ jsx("div", { className: "text-center py-4 text-
|
|
103409
|
+
isLoading && /* @__PURE__ */ jsx("div", { className: "text-center py-4 text-xs text-gray-400 animate-pulse", children: "Loading more elements..." })
|
|
103156
103410
|
] }) }) })
|
|
103157
103411
|
] });
|
|
103158
103412
|
}
|
|
@@ -103185,72 +103439,181 @@ function ElementsPanel() {
|
|
|
103185
103439
|
const [page, setPage] = useState(1);
|
|
103186
103440
|
const [category, setCategory] = useState(null);
|
|
103187
103441
|
const [keyType, setKeyType] = useState(null);
|
|
103188
|
-
const [
|
|
103442
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
103443
|
+
const [mainSearch, setMainSearch] = useState("");
|
|
103444
|
+
const [isViewingAll, setIsViewingAll] = useState(false);
|
|
103445
|
+
const [title, setTitle] = useState("");
|
|
103189
103446
|
const [hasMore, setHasMore] = useState(true);
|
|
103190
103447
|
const scrollRef = useRef(null);
|
|
103191
|
-
const getSVG = async (type = null, categoryValue, pageNo = 1) => {
|
|
103448
|
+
const getSVG = async (type = null, categoryValue = null, searchVal = null, pageNo = 1) => {
|
|
103192
103449
|
startLoading();
|
|
103193
103450
|
if (pageNo === 1) {
|
|
103194
103451
|
setHasMore(true);
|
|
103195
103452
|
setPage(1);
|
|
103196
103453
|
setCategory(categoryValue);
|
|
103454
|
+
setKeyType(type);
|
|
103455
|
+
setSearchQuery(searchVal || "");
|
|
103456
|
+
}
|
|
103457
|
+
if (categoryValue === "Recent items") {
|
|
103458
|
+
const q2 = (searchVal || "").toLowerCase().trim();
|
|
103459
|
+
const filtered = q2 ? recentItems.filter(
|
|
103460
|
+
(item) => item.category?.toLowerCase().includes(q2) || item.tags?.some((t2) => t2.toLowerCase().includes(q2))
|
|
103461
|
+
) : recentItems;
|
|
103462
|
+
setElementsSVG(filtered);
|
|
103463
|
+
setHasMore(false);
|
|
103464
|
+
stopLoading();
|
|
103465
|
+
return;
|
|
103197
103466
|
}
|
|
103198
|
-
|
|
103199
|
-
|
|
103200
|
-
|
|
103201
|
-
|
|
103202
|
-
|
|
103203
|
-
|
|
103204
|
-
|
|
103205
|
-
|
|
103206
|
-
|
|
103207
|
-
|
|
103208
|
-
|
|
103209
|
-
|
|
103210
|
-
|
|
103467
|
+
try {
|
|
103468
|
+
const request = type ? ShapeService.getAllExternal({
|
|
103469
|
+
isVector: true,
|
|
103470
|
+
page: pageNo,
|
|
103471
|
+
limit: 30,
|
|
103472
|
+
keyType: type,
|
|
103473
|
+
query: searchVal || categoryValue || null
|
|
103474
|
+
}) : ShapeService.getAll({
|
|
103475
|
+
category: categoryValue || null,
|
|
103476
|
+
search: searchVal || null,
|
|
103477
|
+
page: pageNo,
|
|
103478
|
+
limit: 30
|
|
103479
|
+
});
|
|
103480
|
+
const res = await request;
|
|
103481
|
+
const items = res.items || [];
|
|
103211
103482
|
const newItems = items.filter((x2) => x2.status && x2.image);
|
|
103212
103483
|
setElementsSVG(
|
|
103213
103484
|
(prev) => pageNo === 1 ? newItems : [...prev, ...newItems]
|
|
103214
103485
|
);
|
|
103215
103486
|
if (newItems.length < 20) setHasMore(false);
|
|
103216
|
-
|
|
103487
|
+
else setHasMore(true);
|
|
103488
|
+
} catch (err) {
|
|
103489
|
+
console.error("Failed to load elements:", err);
|
|
103490
|
+
if (pageNo === 1) setElementsSVG([]);
|
|
103491
|
+
setHasMore(false);
|
|
103492
|
+
} finally {
|
|
103493
|
+
stopLoading();
|
|
103494
|
+
}
|
|
103217
103495
|
};
|
|
103218
103496
|
useEffect(() => {
|
|
103219
103497
|
const el = scrollRef.current;
|
|
103220
|
-
if (!el) return;
|
|
103498
|
+
if (!el || !isViewingAll) return;
|
|
103221
103499
|
const onScroll = () => {
|
|
103222
103500
|
if (!hasMore || isLoading) return;
|
|
103223
103501
|
if (el.scrollTop + el.clientHeight >= el.scrollHeight - 200) {
|
|
103224
|
-
|
|
103225
|
-
|
|
103226
|
-
|
|
103502
|
+
setPage((prev) => {
|
|
103503
|
+
const next = prev + 1;
|
|
103504
|
+
getSVG(keyType, category, searchQuery, next);
|
|
103505
|
+
return next;
|
|
103506
|
+
});
|
|
103227
103507
|
}
|
|
103228
103508
|
};
|
|
103229
103509
|
el.addEventListener("scroll", onScroll);
|
|
103230
103510
|
return () => el.removeEventListener("scroll", onScroll);
|
|
103231
|
-
}, [hasMore, isLoading]);
|
|
103232
|
-
|
|
103511
|
+
}, [hasMore, isLoading, isViewingAll, keyType, category, searchQuery]);
|
|
103512
|
+
const handleMainSearch = (val) => {
|
|
103513
|
+
const q2 = val.trim();
|
|
103514
|
+
if (!q2) return;
|
|
103515
|
+
setIsViewingAll(true);
|
|
103516
|
+
setTitle(`Search: "${q2}"`);
|
|
103517
|
+
getSVG(null, null, q2, 1);
|
|
103518
|
+
};
|
|
103519
|
+
const handleBack = () => {
|
|
103520
|
+
if (searchQuery && category && category !== "Recent items") {
|
|
103521
|
+
setSearchQuery("");
|
|
103522
|
+
setPage(1);
|
|
103523
|
+
setTitle(category);
|
|
103524
|
+
getSVG(keyType, category, "", 1);
|
|
103525
|
+
return;
|
|
103526
|
+
}
|
|
103527
|
+
setIsViewingAll(false);
|
|
103528
|
+
setElementsSVG([]);
|
|
103529
|
+
setCategory(null);
|
|
103530
|
+
setKeyType(null);
|
|
103531
|
+
setSearchQuery("");
|
|
103532
|
+
setMainSearch("");
|
|
103533
|
+
setTitle("");
|
|
103534
|
+
setPage(1);
|
|
103535
|
+
setHasMore(true);
|
|
103536
|
+
};
|
|
103537
|
+
if (isViewingAll) {
|
|
103233
103538
|
return /* @__PURE__ */ jsx(
|
|
103234
103539
|
AllElements,
|
|
103235
103540
|
{
|
|
103236
103541
|
elements: elementsSVG,
|
|
103237
103542
|
scrollRef,
|
|
103543
|
+
title,
|
|
103544
|
+
backText: title || "Elements",
|
|
103545
|
+
initialSearch: searchQuery,
|
|
103238
103546
|
onSearch: (value) => {
|
|
103239
|
-
|
|
103240
|
-
|
|
103241
|
-
|
|
103547
|
+
if (value.trim()) {
|
|
103548
|
+
setTitle(category ? `${category} - "${value.trim()}"` : `Search: "${value.trim()}"`);
|
|
103549
|
+
} else {
|
|
103550
|
+
setTitle(
|
|
103551
|
+
category || (keyType === "pixabay" ? "Pixabay Vectors" : keyType === "flat" ? "FlatIcon Icons" : "All Elements")
|
|
103552
|
+
);
|
|
103553
|
+
}
|
|
103554
|
+
getSVG(keyType, category, value, 1);
|
|
103242
103555
|
},
|
|
103243
|
-
onBack:
|
|
103244
|
-
setElementsSVG([]);
|
|
103245
|
-
setPage(1);
|
|
103246
|
-
setCategory(null);
|
|
103247
|
-
setKeyType(null);
|
|
103248
|
-
setHasMore(true);
|
|
103249
|
-
}
|
|
103556
|
+
onBack: handleBack
|
|
103250
103557
|
}
|
|
103251
103558
|
);
|
|
103252
103559
|
}
|
|
103253
103560
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
103561
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-neutral-800 px-3 py-2 rounded flex items-center gap-2 mb-4 mx-4 relative focus-within:ring-1 focus-within:ring-neutral-600", children: [
|
|
103562
|
+
/* @__PURE__ */ jsxs(
|
|
103563
|
+
"svg",
|
|
103564
|
+
{
|
|
103565
|
+
width: "18",
|
|
103566
|
+
stroke: "currentColor",
|
|
103567
|
+
fill: "none",
|
|
103568
|
+
viewBox: "0 0 24 24",
|
|
103569
|
+
className: "text-gray-400 shrink-0",
|
|
103570
|
+
children: [
|
|
103571
|
+
/* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
|
|
103572
|
+
/* @__PURE__ */ jsx("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })
|
|
103573
|
+
]
|
|
103574
|
+
}
|
|
103575
|
+
),
|
|
103576
|
+
/* @__PURE__ */ jsx(
|
|
103577
|
+
"input",
|
|
103578
|
+
{
|
|
103579
|
+
type: "text",
|
|
103580
|
+
placeholder: "Search elements & shapes...",
|
|
103581
|
+
value: mainSearch,
|
|
103582
|
+
onChange: (e3) => setMainSearch(e3.target.value),
|
|
103583
|
+
onKeyDown: (e3) => {
|
|
103584
|
+
e3.stopPropagation();
|
|
103585
|
+
if (e3.key === "Enter" && mainSearch.trim()) {
|
|
103586
|
+
handleMainSearch(mainSearch.trim());
|
|
103587
|
+
}
|
|
103588
|
+
},
|
|
103589
|
+
className: "bg-transparent outline-none text-sm text-gray-200 placeholder-gray-500 w-full pr-7"
|
|
103590
|
+
}
|
|
103591
|
+
),
|
|
103592
|
+
mainSearch && /* @__PURE__ */ jsx(
|
|
103593
|
+
"button",
|
|
103594
|
+
{
|
|
103595
|
+
type: "button",
|
|
103596
|
+
onClick: () => setMainSearch(""),
|
|
103597
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-white transition-colors p-1 cursor-pointer",
|
|
103598
|
+
"aria-label": "Clear search",
|
|
103599
|
+
children: /* @__PURE__ */ jsxs(
|
|
103600
|
+
"svg",
|
|
103601
|
+
{
|
|
103602
|
+
width: "14",
|
|
103603
|
+
height: "14",
|
|
103604
|
+
viewBox: "0 0 24 24",
|
|
103605
|
+
fill: "none",
|
|
103606
|
+
stroke: "currentColor",
|
|
103607
|
+
strokeWidth: "2",
|
|
103608
|
+
children: [
|
|
103609
|
+
/* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
103610
|
+
/* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
103611
|
+
]
|
|
103612
|
+
}
|
|
103613
|
+
)
|
|
103614
|
+
}
|
|
103615
|
+
)
|
|
103616
|
+
] }),
|
|
103254
103617
|
/* @__PURE__ */ jsxs("div", { className: "pb-5 mx-4", children: [
|
|
103255
103618
|
/* @__PURE__ */ jsx("h2", { className: "text-md font-medium mb-3", children: "Upload From Other Media" }),
|
|
103256
103619
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
@@ -103258,11 +103621,13 @@ function ElementsPanel() {
|
|
|
103258
103621
|
"button",
|
|
103259
103622
|
{
|
|
103260
103623
|
type: "button",
|
|
103261
|
-
className: "cursor-pointer",
|
|
103624
|
+
className: "cursor-pointer hover:opacity-80 transition-opacity",
|
|
103262
103625
|
onClick: () => {
|
|
103263
|
-
|
|
103264
|
-
|
|
103626
|
+
setIsViewingAll(true);
|
|
103627
|
+
setTitle("Pixabay Vectors");
|
|
103628
|
+
getSVG("pixabay", "", "", 1);
|
|
103265
103629
|
},
|
|
103630
|
+
title: "Search Pixabay Vectors",
|
|
103266
103631
|
children: /* @__PURE__ */ jsx(PixabayIcon, {})
|
|
103267
103632
|
}
|
|
103268
103633
|
),
|
|
@@ -103270,11 +103635,13 @@ function ElementsPanel() {
|
|
|
103270
103635
|
"button",
|
|
103271
103636
|
{
|
|
103272
103637
|
type: "button",
|
|
103273
|
-
className: "cursor-pointer",
|
|
103638
|
+
className: "cursor-pointer hover:opacity-80 transition-opacity",
|
|
103274
103639
|
onClick: () => {
|
|
103275
|
-
|
|
103276
|
-
|
|
103640
|
+
setIsViewingAll(true);
|
|
103641
|
+
setTitle("FlatIcon Icons");
|
|
103642
|
+
getSVG("flat", "", "", 1);
|
|
103277
103643
|
},
|
|
103644
|
+
title: "Search FlatIcon Icons",
|
|
103278
103645
|
children: /* @__PURE__ */ jsx(
|
|
103279
103646
|
"img",
|
|
103280
103647
|
{
|
|
@@ -103289,20 +103656,21 @@ function ElementsPanel() {
|
|
|
103289
103656
|
)
|
|
103290
103657
|
] })
|
|
103291
103658
|
] }),
|
|
103292
|
-
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px]" }),
|
|
103293
|
-
/* @__PURE__ */ jsxs("div", { className: "h-[calc(100dvh-
|
|
103659
|
+
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px] border-neutral-800" }),
|
|
103660
|
+
/* @__PURE__ */ jsxs("div", { className: "h-[calc(100dvh-200px)] overflow-auto custom-scrollbar", children: [
|
|
103294
103661
|
recentItems.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mx-4", children: [
|
|
103295
103662
|
/* @__PURE__ */ jsxs("div", { className: "py-5", children: [
|
|
103296
103663
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
103297
103664
|
/* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold mb-3", children: "Recent items" }),
|
|
103298
103665
|
recentItems.length > 3 && /* @__PURE__ */ jsx(
|
|
103299
|
-
"
|
|
103666
|
+
"button",
|
|
103300
103667
|
{
|
|
103301
|
-
|
|
103668
|
+
type: "button",
|
|
103669
|
+
className: "underline text-xs text-neutral-400 hover:text-white cursor-pointer",
|
|
103302
103670
|
onClick: () => {
|
|
103303
|
-
|
|
103304
|
-
|
|
103305
|
-
|
|
103671
|
+
setIsViewingAll(true);
|
|
103672
|
+
setTitle("Recent items");
|
|
103673
|
+
getSVG(null, "Recent items", "", 1);
|
|
103306
103674
|
},
|
|
103307
103675
|
children: "View all"
|
|
103308
103676
|
}
|
|
@@ -103311,36 +103679,45 @@ function ElementsPanel() {
|
|
|
103311
103679
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 md:grid-cols-3 gap-3", children: recentItems.slice(0, 3).map((shape, i) => /* @__PURE__ */ jsx(
|
|
103312
103680
|
"button",
|
|
103313
103681
|
{
|
|
103682
|
+
type: "button",
|
|
103314
103683
|
onClick: () => {
|
|
103315
103684
|
addSVGToCanvas("svg", shape.image);
|
|
103316
103685
|
addRecentItem(shape);
|
|
103686
|
+
if (window.innerWidth < 768) {
|
|
103687
|
+
useSidePanelStore.getState().setActivePanel(null);
|
|
103688
|
+
}
|
|
103317
103689
|
},
|
|
103318
|
-
className: "w-full cursor-pointer flex items-center justify-center",
|
|
103690
|
+
className: "w-full cursor-pointer flex items-center justify-center p-2 rounded-lg bg-neutral-900/40 hover:bg-neutral-800 border border-neutral-800/60 hover:border-neutral-700 transition-colors",
|
|
103319
103691
|
children: /* @__PURE__ */ jsx(
|
|
103320
103692
|
"img",
|
|
103321
103693
|
{
|
|
103322
103694
|
src: shape.image,
|
|
103323
|
-
alt: shape.category,
|
|
103695
|
+
alt: shape.category || "recent element",
|
|
103324
103696
|
width: 150,
|
|
103325
103697
|
height: 150,
|
|
103326
|
-
className: "w-full h-
|
|
103698
|
+
className: "w-full h-20 object-contain"
|
|
103327
103699
|
}
|
|
103328
103700
|
)
|
|
103329
103701
|
},
|
|
103330
103702
|
shape._id || shape.image || i
|
|
103331
103703
|
)) })
|
|
103332
103704
|
] }),
|
|
103333
|
-
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px]" })
|
|
103705
|
+
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px] border-neutral-800" })
|
|
103334
103706
|
] }),
|
|
103335
103707
|
elements.map((item, i) => /* @__PURE__ */ jsxs("div", { className: "mx-4", children: [
|
|
103336
103708
|
/* @__PURE__ */ jsxs("div", { className: "py-5", children: [
|
|
103337
103709
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
103338
103710
|
/* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold mb-3", children: item.category }),
|
|
103339
103711
|
/* @__PURE__ */ jsx(
|
|
103340
|
-
"
|
|
103712
|
+
"button",
|
|
103341
103713
|
{
|
|
103342
|
-
|
|
103343
|
-
|
|
103714
|
+
type: "button",
|
|
103715
|
+
className: "underline text-xs text-neutral-400 hover:text-white cursor-pointer",
|
|
103716
|
+
onClick: () => {
|
|
103717
|
+
setIsViewingAll(true);
|
|
103718
|
+
setTitle(item.category);
|
|
103719
|
+
getSVG(null, item.category, "", 1);
|
|
103720
|
+
},
|
|
103344
103721
|
children: "View all"
|
|
103345
103722
|
}
|
|
103346
103723
|
)
|
|
@@ -103348,6 +103725,7 @@ function ElementsPanel() {
|
|
|
103348
103725
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 md:grid-cols-3 gap-3", children: item.data.map((shape, j2) => /* @__PURE__ */ jsx(
|
|
103349
103726
|
"button",
|
|
103350
103727
|
{
|
|
103728
|
+
type: "button",
|
|
103351
103729
|
onClick: () => {
|
|
103352
103730
|
addSVGToCanvas("svg", shape.image);
|
|
103353
103731
|
addRecentItem({
|
|
@@ -103358,8 +103736,11 @@ function ElementsPanel() {
|
|
|
103358
103736
|
largeImage: shape.image,
|
|
103359
103737
|
tags: []
|
|
103360
103738
|
});
|
|
103739
|
+
if (window.innerWidth < 768) {
|
|
103740
|
+
useSidePanelStore.getState().setActivePanel(null);
|
|
103741
|
+
}
|
|
103361
103742
|
},
|
|
103362
|
-
className: "w-full cursor-pointer flex items-center justify-center",
|
|
103743
|
+
className: "w-full cursor-pointer flex items-center justify-center p-2 rounded-lg bg-neutral-900/40 hover:bg-neutral-800 border border-neutral-800/60 hover:border-neutral-700 transition-colors",
|
|
103363
103744
|
children: /* @__PURE__ */ jsx(
|
|
103364
103745
|
"img",
|
|
103365
103746
|
{
|
|
@@ -103367,14 +103748,15 @@ function ElementsPanel() {
|
|
|
103367
103748
|
alt: item.category,
|
|
103368
103749
|
width: 150,
|
|
103369
103750
|
height: 150,
|
|
103370
|
-
className: "w-full h-
|
|
103751
|
+
className: "w-full h-20 object-contain",
|
|
103752
|
+
loading: "lazy"
|
|
103371
103753
|
}
|
|
103372
103754
|
)
|
|
103373
103755
|
},
|
|
103374
103756
|
j2
|
|
103375
103757
|
)) })
|
|
103376
103758
|
] }),
|
|
103377
|
-
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px]" })
|
|
103759
|
+
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px] border-neutral-800" })
|
|
103378
103760
|
] }, i))
|
|
103379
103761
|
] })
|
|
103380
103762
|
] });
|
|
@@ -103387,7 +103769,7 @@ async function addTextToCanvas(fontSize, width, textName) {
|
|
|
103387
103769
|
if (!workarea) return;
|
|
103388
103770
|
const workareaCenter = workarea.getCenterPoint();
|
|
103389
103771
|
const workareaBounds = workarea.getBoundingRect();
|
|
103390
|
-
const maxTextWidth = Math.max(40, workareaBounds.width * 0.
|
|
103772
|
+
const maxTextWidth = Math.max(40, workareaBounds.width * 0.85);
|
|
103391
103773
|
const textWidth = Math.min(width, maxTextWidth);
|
|
103392
103774
|
const text = new Uo(textName, {
|
|
103393
103775
|
fontSize,
|
|
@@ -103400,19 +103782,26 @@ async function addTextToCanvas(fontSize, width, textName) {
|
|
|
103400
103782
|
strokeWidth: 0,
|
|
103401
103783
|
hasRotatingPoint: true,
|
|
103402
103784
|
centerTransform: true,
|
|
103403
|
-
padding:
|
|
103785
|
+
padding: 0,
|
|
103404
103786
|
strokeUniform: true,
|
|
103405
103787
|
paintFirst: "stroke",
|
|
103406
103788
|
objectCaching: false
|
|
103407
103789
|
// Ensure maximum sharpness
|
|
103408
103790
|
});
|
|
103791
|
+
const measuredWidth = Math.ceil(text.calcTextWidth());
|
|
103792
|
+
if (measuredWidth > 0 && !textName.includes("\n")) {
|
|
103793
|
+
text.set({ width: Math.min(Math.max(measuredWidth + 4, 20), maxTextWidth) });
|
|
103794
|
+
text.initDimensions();
|
|
103795
|
+
}
|
|
103409
103796
|
canvas2.add(text);
|
|
103410
103797
|
text.set({
|
|
103411
103798
|
left: workareaCenter.x - text.getScaledWidth() / 2,
|
|
103412
103799
|
top: workareaCenter.y - text.getScaledHeight() / 2
|
|
103413
103800
|
});
|
|
103414
103801
|
canvas2.setActiveObject(text);
|
|
103802
|
+
bringOverlaysToFront(canvas2);
|
|
103415
103803
|
canvas2.requestRenderAll();
|
|
103804
|
+
useHistoryStore.getState().saveState();
|
|
103416
103805
|
}
|
|
103417
103806
|
async function addTextTemplate(id) {
|
|
103418
103807
|
try {
|
|
@@ -103444,6 +103833,7 @@ async function addTextTemplate(id) {
|
|
|
103444
103833
|
canvas2.add(...fabricObjects);
|
|
103445
103834
|
canvas2.renderOnAddRemove = true;
|
|
103446
103835
|
canvas2.requestRenderAll();
|
|
103836
|
+
useHistoryStore.getState().saveState();
|
|
103447
103837
|
} catch (err) {
|
|
103448
103838
|
console.error("Failed to add text template:", err);
|
|
103449
103839
|
}
|
|
@@ -104211,11 +104601,20 @@ function PhotosPanel() {
|
|
|
104211
104601
|
}) : !isLoading ? /* @__PURE__ */ jsx("div", { className: "col-span-full py-8 text-center text-xs text-gray-500", children: "No images found" }) : null }) })
|
|
104212
104602
|
] });
|
|
104213
104603
|
}
|
|
104604
|
+
let bgSaveTimer = null;
|
|
104605
|
+
function flushBackgroundSave() {
|
|
104606
|
+
if (bgSaveTimer) {
|
|
104607
|
+
clearTimeout(bgSaveTimer);
|
|
104608
|
+
bgSaveTimer = null;
|
|
104609
|
+
useHistoryStore.getState().saveState();
|
|
104610
|
+
}
|
|
104611
|
+
}
|
|
104214
104612
|
async function addBackgroundImage(url) {
|
|
104215
104613
|
const canvas2 = useEditorStore.getState().canvas;
|
|
104216
104614
|
if (!canvas2) return;
|
|
104217
104615
|
const workarea = canvas2.getObjects().find((o2) => o2.name === "workarea");
|
|
104218
104616
|
if (!workarea) return;
|
|
104617
|
+
flushBackgroundSave();
|
|
104219
104618
|
workarea.set("fill", "#ffffff");
|
|
104220
104619
|
canvas2.getObjects("image").forEach((o2) => {
|
|
104221
104620
|
if (o2.name === "bg-image") canvas2.remove(o2);
|
|
@@ -104227,7 +104626,16 @@ async function addBackgroundImage(url) {
|
|
|
104227
104626
|
const activeSide = workarea.__pageSide || useHistoryStore.getState().currentPage || "front";
|
|
104228
104627
|
const baseRx = (workarea.rx || workarea.__radiusOfCanvas || 0) * (workarea.scaleX || 1);
|
|
104229
104628
|
const baseRy = (workarea.ry || workarea.__radiusOfCanvas || 0) * (workarea.scaleY || 1);
|
|
104230
|
-
|
|
104629
|
+
let finalUrl = url;
|
|
104630
|
+
try {
|
|
104631
|
+
const b64 = await getBase64ImageFromURL(url);
|
|
104632
|
+
if (b64 && (b64.startsWith("data:image/png") || b64.startsWith("data:image/jpeg"))) {
|
|
104633
|
+
finalUrl = b64;
|
|
104634
|
+
}
|
|
104635
|
+
} catch {
|
|
104636
|
+
}
|
|
104637
|
+
try {
|
|
104638
|
+
const img = await oa.fromURL(finalUrl, { crossOrigin: "anonymous" });
|
|
104231
104639
|
const imgW = img.width || 1;
|
|
104232
104640
|
const imgH = img.height || 1;
|
|
104233
104641
|
const scale2 = Math.max(waW / imgW, waH / imgH);
|
|
@@ -104263,12 +104671,14 @@ async function addBackgroundImage(url) {
|
|
|
104263
104671
|
canvas2.add(img);
|
|
104264
104672
|
canvas2.sendObjectToBack(img);
|
|
104265
104673
|
canvas2.sendObjectToBack(workarea);
|
|
104674
|
+
bringOverlaysToFront(canvas2);
|
|
104266
104675
|
canvas2.requestRenderAll();
|
|
104267
|
-
|
|
104676
|
+
useHistoryStore.getState().saveState();
|
|
104677
|
+
} catch (err) {
|
|
104268
104678
|
console.warn("Fabric failed to load background image:", url, err);
|
|
104269
|
-
}
|
|
104679
|
+
}
|
|
104270
104680
|
}
|
|
104271
|
-
async function addBackgroundColor(color) {
|
|
104681
|
+
async function addBackgroundColor(color, immediate = false) {
|
|
104272
104682
|
const canvas2 = useEditorStore.getState().canvas;
|
|
104273
104683
|
if (!canvas2) return;
|
|
104274
104684
|
const workarea = canvas2.getObjects().find((o2) => o2.name === "workarea");
|
|
@@ -104277,7 +104687,20 @@ async function addBackgroundColor(color) {
|
|
|
104277
104687
|
if (o2.name === "bg-image") canvas2.remove(o2);
|
|
104278
104688
|
});
|
|
104279
104689
|
workarea.set("fill", color);
|
|
104690
|
+
bringOverlaysToFront(canvas2);
|
|
104280
104691
|
canvas2.requestRenderAll();
|
|
104692
|
+
if (bgSaveTimer) {
|
|
104693
|
+
clearTimeout(bgSaveTimer);
|
|
104694
|
+
bgSaveTimer = null;
|
|
104695
|
+
}
|
|
104696
|
+
if (immediate) {
|
|
104697
|
+
useHistoryStore.getState().saveState();
|
|
104698
|
+
} else {
|
|
104699
|
+
bgSaveTimer = setTimeout(() => {
|
|
104700
|
+
useHistoryStore.getState().saveState();
|
|
104701
|
+
bgSaveTimer = null;
|
|
104702
|
+
}, 120);
|
|
104703
|
+
}
|
|
104281
104704
|
}
|
|
104282
104705
|
function normalizeHexColor(c2) {
|
|
104283
104706
|
if (!c2 || typeof c2 !== "string" || c2 === "none" || c2 === "transparent" || c2.trim() === "") return null;
|
|
@@ -106037,11 +106460,11 @@ function BackgroundPanel() {
|
|
|
106037
106460
|
const [hasMore, setHasMore] = useState(true);
|
|
106038
106461
|
const [page, setPage] = useState(1);
|
|
106039
106462
|
const scrollRef = useRef(null);
|
|
106040
|
-
const handleApplyBackground = (type, value) => {
|
|
106463
|
+
const handleApplyBackground = async (type, value, immediate = false) => {
|
|
106041
106464
|
if (type === "color") {
|
|
106042
|
-
addBackgroundColor(value);
|
|
106465
|
+
await addBackgroundColor(value, immediate);
|
|
106043
106466
|
} else {
|
|
106044
|
-
addBackgroundImage(value);
|
|
106467
|
+
await addBackgroundImage(value);
|
|
106045
106468
|
}
|
|
106046
106469
|
if (window.innerWidth < 768) {
|
|
106047
106470
|
setActivePanel(null);
|
|
@@ -106094,7 +106517,7 @@ function BackgroundPanel() {
|
|
|
106094
106517
|
CMYKColorPicker,
|
|
106095
106518
|
{
|
|
106096
106519
|
value: "#ffffff",
|
|
106097
|
-
onChange: (hex) => handleApplyBackground("color", hex)
|
|
106520
|
+
onChange: (hex) => handleApplyBackground("color", hex, false)
|
|
106098
106521
|
}
|
|
106099
106522
|
),
|
|
106100
106523
|
CMYK_SAFE_COLORS.map(({ hex, label }) => /* @__PURE__ */ jsx(
|
|
@@ -106102,7 +106525,7 @@ function BackgroundPanel() {
|
|
|
106102
106525
|
{
|
|
106103
106526
|
className: "w-8 h-8 rounded border border-neutral-600 cursor-pointer transition-transform hover:scale-110",
|
|
106104
106527
|
style: { backgroundColor: hex },
|
|
106105
|
-
onClick: () => handleApplyBackground("color", hex),
|
|
106528
|
+
onClick: () => handleApplyBackground("color", hex, true),
|
|
106106
106529
|
title: label
|
|
106107
106530
|
},
|
|
106108
106531
|
hex
|
|
@@ -106118,7 +106541,7 @@ function BackgroundPanel() {
|
|
|
106118
106541
|
const image = await ShapeService.convertImages(item.largeImage);
|
|
106119
106542
|
const imageUrl = image?.success ? image?.url : null;
|
|
106120
106543
|
if (!imageUrl) return;
|
|
106121
|
-
handleApplyBackground("image", imageUrl);
|
|
106544
|
+
await handleApplyBackground("image", imageUrl);
|
|
106122
106545
|
if (window.innerWidth < 768) {
|
|
106123
106546
|
useSidePanelStore.getState().setActivePanel(null);
|
|
106124
106547
|
}
|
|
@@ -106214,24 +106637,6 @@ function UploadPanel() {
|
|
|
106214
106637
|
)) })
|
|
106215
106638
|
] });
|
|
106216
106639
|
}
|
|
106217
|
-
const useDrawStore = create((set) => ({
|
|
106218
|
-
drawTool: "select",
|
|
106219
|
-
strokeWidth: 5,
|
|
106220
|
-
opacity: 100,
|
|
106221
|
-
color: "#000000",
|
|
106222
|
-
setDrawTool: (tool) => set(() => {
|
|
106223
|
-
if (tool === "highlighter") {
|
|
106224
|
-
return { drawTool: tool, opacity: 50 };
|
|
106225
|
-
}
|
|
106226
|
-
if (tool === "brush") {
|
|
106227
|
-
return { drawTool: tool, opacity: 100 };
|
|
106228
|
-
}
|
|
106229
|
-
return { drawTool: tool };
|
|
106230
|
-
}),
|
|
106231
|
-
setStrokeWidth: (v2) => set({ strokeWidth: v2 }),
|
|
106232
|
-
setOpacity: (v2) => set({ opacity: v2 }),
|
|
106233
|
-
setColor: (v2) => set({ color: v2 })
|
|
106234
|
-
}));
|
|
106235
106640
|
function DrawPanel() {
|
|
106236
106641
|
const {
|
|
106237
106642
|
drawTool,
|
|
@@ -106246,7 +106651,7 @@ function DrawPanel() {
|
|
|
106246
106651
|
const isDisabled = drawTool === "select";
|
|
106247
106652
|
const TOOLS = [
|
|
106248
106653
|
{ key: "select", label: "✦ Selection" },
|
|
106249
|
-
{ key: "brush", label: "
|
|
106654
|
+
{ key: "brush", label: "✏️ Brush" },
|
|
106250
106655
|
{ key: "highlighter", label: "✎ Highlighter" }
|
|
106251
106656
|
];
|
|
106252
106657
|
return /* @__PURE__ */ jsxs("div", { className: "mx-4 select-none", children: [
|
|
@@ -111037,8 +111442,8 @@ function MaskPanel() {
|
|
|
111037
111442
|
const objWidth = obj.width ?? 1;
|
|
111038
111443
|
const objHeight = obj.height ?? 1;
|
|
111039
111444
|
const pathDims = clipPath.getBoundingRect();
|
|
111040
|
-
const scaleX = objWidth / pathDims.width;
|
|
111041
|
-
const scaleY = objHeight / pathDims.height;
|
|
111445
|
+
const scaleX = objWidth / (clipPath.width || pathDims.width);
|
|
111446
|
+
const scaleY = objHeight / (clipPath.height || pathDims.height);
|
|
111042
111447
|
clipPath.set({
|
|
111043
111448
|
scaleX,
|
|
111044
111449
|
scaleY,
|
|
@@ -111047,6 +111452,8 @@ function MaskPanel() {
|
|
|
111047
111452
|
angle: 0
|
|
111048
111453
|
// Should not inherit angle since the parent handles rotation
|
|
111049
111454
|
});
|
|
111455
|
+
clipPath.__maskPath = svgPath;
|
|
111456
|
+
obj.__maskPath = svgPath;
|
|
111050
111457
|
obj.set({ clipPath });
|
|
111051
111458
|
obj.setCoords();
|
|
111052
111459
|
canvas2.requestRenderAll();
|
|
@@ -156997,7 +157404,7 @@ function SidePanel() {
|
|
|
156997
157404
|
id: item.id,
|
|
156998
157405
|
item,
|
|
156999
157406
|
isActive: activePanel === item.id,
|
|
157000
|
-
onClick: () => setActivePanel(item.id)
|
|
157407
|
+
onClick: () => setActivePanel(activePanel === item.id ? null : item.id)
|
|
157001
157408
|
},
|
|
157002
157409
|
item.id
|
|
157003
157410
|
)
|
|
@@ -157012,7 +157419,7 @@ function SidePanel() {
|
|
|
157012
157419
|
item,
|
|
157013
157420
|
variant: "mobile",
|
|
157014
157421
|
isActive: activePanel === item.id,
|
|
157015
|
-
onClick: () => setActivePanel(item.id)
|
|
157422
|
+
onClick: () => setActivePanel(activePanel === item.id ? null : item.id)
|
|
157016
157423
|
},
|
|
157017
157424
|
item.id
|
|
157018
157425
|
)
|
|
@@ -157138,20 +157545,48 @@ function TextFontSize({
|
|
|
157138
157545
|
}
|
|
157139
157546
|
);
|
|
157140
157547
|
}
|
|
157141
|
-
function
|
|
157548
|
+
function TextAlignGroup({
|
|
157142
157549
|
value,
|
|
157143
157550
|
onChange
|
|
157144
157551
|
}) {
|
|
157145
|
-
const
|
|
157552
|
+
const current = value === "center" || value === "right" ? value : "left";
|
|
157146
157553
|
return /* @__PURE__ */ jsxs(
|
|
157147
|
-
"
|
|
157554
|
+
"div",
|
|
157148
157555
|
{
|
|
157149
|
-
|
|
157150
|
-
|
|
157556
|
+
className: "flex items-center bg-neutral-800 rounded p-0.5 gap-0.5 border border-neutral-700",
|
|
157557
|
+
role: "group",
|
|
157558
|
+
"aria-label": "Text alignment",
|
|
157151
157559
|
children: [
|
|
157152
|
-
|
|
157153
|
-
|
|
157154
|
-
|
|
157560
|
+
/* @__PURE__ */ jsx(
|
|
157561
|
+
"button",
|
|
157562
|
+
{
|
|
157563
|
+
type: "button",
|
|
157564
|
+
title: "Align Left",
|
|
157565
|
+
onClick: () => onChange("left"),
|
|
157566
|
+
className: `p-1 rounded transition-colors ${current === "left" ? "bg-neutral-600 text-white shadow-sm" : "text-neutral-400 hover:text-white hover:bg-neutral-700"}`,
|
|
157567
|
+
children: /* @__PURE__ */ jsx(AlignLeftIcon$1, {})
|
|
157568
|
+
}
|
|
157569
|
+
),
|
|
157570
|
+
/* @__PURE__ */ jsx(
|
|
157571
|
+
"button",
|
|
157572
|
+
{
|
|
157573
|
+
type: "button",
|
|
157574
|
+
title: "Align Center",
|
|
157575
|
+
onClick: () => onChange("center"),
|
|
157576
|
+
className: `p-1 rounded transition-colors ${current === "center" ? "bg-neutral-600 text-white shadow-sm" : "text-neutral-400 hover:text-white hover:bg-neutral-700"}`,
|
|
157577
|
+
children: /* @__PURE__ */ jsx(AlignCenterIcon$1, {})
|
|
157578
|
+
}
|
|
157579
|
+
),
|
|
157580
|
+
/* @__PURE__ */ jsx(
|
|
157581
|
+
"button",
|
|
157582
|
+
{
|
|
157583
|
+
type: "button",
|
|
157584
|
+
title: "Align Right",
|
|
157585
|
+
onClick: () => onChange("right"),
|
|
157586
|
+
className: `p-1 rounded transition-colors ${current === "right" ? "bg-neutral-600 text-white shadow-sm" : "text-neutral-400 hover:text-white hover:bg-neutral-700"}`,
|
|
157587
|
+
children: /* @__PURE__ */ jsx(AlignRightIcon$1, {})
|
|
157588
|
+
}
|
|
157589
|
+
)
|
|
157155
157590
|
]
|
|
157156
157591
|
}
|
|
157157
157592
|
);
|
|
@@ -157204,15 +157639,29 @@ function TextToolbar() {
|
|
|
157204
157639
|
}, [type, objects]);
|
|
157205
157640
|
useEffect(() => {
|
|
157206
157641
|
if (!text) return;
|
|
157207
|
-
|
|
157208
|
-
|
|
157209
|
-
|
|
157210
|
-
|
|
157211
|
-
|
|
157212
|
-
|
|
157213
|
-
|
|
157214
|
-
|
|
157215
|
-
|
|
157642
|
+
const syncProps = () => {
|
|
157643
|
+
setFontSize(text.fontSize ?? 18);
|
|
157644
|
+
setLineHeight2((text.lineHeight ?? 1) * 100);
|
|
157645
|
+
setCharSpacing(text.charSpacing ?? 0);
|
|
157646
|
+
setAlign(text.textAlign ?? "left");
|
|
157647
|
+
const dashArray = text.strokeDashArray;
|
|
157648
|
+
const currentStrokeStyle = Array.isArray(dashArray) && dashArray.length === 2 && dashArray[0] === 2 && dashArray[1] === 4 ? "Dotted" : Array.isArray(dashArray) && dashArray.length === 2 && dashArray[0] === 10 && dashArray[1] === 5 ? "Dashed" : "Line";
|
|
157649
|
+
setStrokeColor(typeof text.stroke === "string" && text.stroke ? text.stroke : "#000000");
|
|
157650
|
+
setStrokeSize(Math.max(0, Number(text.strokeWidth ?? 0)));
|
|
157651
|
+
setStrokeStyle(currentStrokeStyle);
|
|
157652
|
+
};
|
|
157653
|
+
syncProps();
|
|
157654
|
+
const canvas2 = text.canvas;
|
|
157655
|
+
if (canvas2) {
|
|
157656
|
+
canvas2.on("object:modified", syncProps);
|
|
157657
|
+
canvas2.on("selection:updated", syncProps);
|
|
157658
|
+
}
|
|
157659
|
+
return () => {
|
|
157660
|
+
if (canvas2) {
|
|
157661
|
+
canvas2.off("object:modified", syncProps);
|
|
157662
|
+
canvas2.off("selection:updated", syncProps);
|
|
157663
|
+
}
|
|
157664
|
+
};
|
|
157216
157665
|
}, [text]);
|
|
157217
157666
|
const updateText = useCallback(
|
|
157218
157667
|
(props) => {
|
|
@@ -157220,6 +157669,11 @@ function TextToolbar() {
|
|
|
157220
157669
|
const canvas2 = text.canvas;
|
|
157221
157670
|
if (!canvas2) return;
|
|
157222
157671
|
text.set(props);
|
|
157672
|
+
if (props.textAlign) {
|
|
157673
|
+
text.dirty = true;
|
|
157674
|
+
text.initDimensions?.();
|
|
157675
|
+
text.setCoords();
|
|
157676
|
+
}
|
|
157223
157677
|
canvas2.requestRenderAll();
|
|
157224
157678
|
useHistoryStore.getState().saveState();
|
|
157225
157679
|
},
|
|
@@ -157276,7 +157730,7 @@ function TextToolbar() {
|
|
|
157276
157730
|
}
|
|
157277
157731
|
),
|
|
157278
157732
|
/* @__PURE__ */ jsx(
|
|
157279
|
-
|
|
157733
|
+
TextAlignGroup,
|
|
157280
157734
|
{
|
|
157281
157735
|
value: align,
|
|
157282
157736
|
onChange: (v2) => {
|
|
@@ -157532,6 +157986,7 @@ class CropService {
|
|
|
157532
157986
|
darkOverlay = null;
|
|
157533
157987
|
focusedImage = null;
|
|
157534
157988
|
cropZone = null;
|
|
157989
|
+
maskPath = null;
|
|
157535
157990
|
onEnd = null;
|
|
157536
157991
|
init(canvas2) {
|
|
157537
157992
|
this.canvas = canvas2;
|
|
@@ -157543,6 +157998,7 @@ class CropService {
|
|
|
157543
157998
|
this.cleanup();
|
|
157544
157999
|
this.targetImage = image;
|
|
157545
158000
|
this.onEnd = onEnd || null;
|
|
158001
|
+
this.maskPath = image.__maskPath || image.clipPath?.__maskPath || null;
|
|
157546
158002
|
const canvas2 = this.canvas;
|
|
157547
158003
|
canvas2.renderOnAddRemove = false;
|
|
157548
158004
|
canvas2.discardActiveObject();
|
|
@@ -157559,7 +158015,8 @@ class CropService {
|
|
|
157559
158015
|
width: naturalWidth,
|
|
157560
158016
|
height: naturalHeight,
|
|
157561
158017
|
selectable: false,
|
|
157562
|
-
evented: false
|
|
158018
|
+
evented: false,
|
|
158019
|
+
clipPath: void 0
|
|
157563
158020
|
});
|
|
157564
158021
|
const cropX = image.cropX || 0;
|
|
157565
158022
|
const cropY = image.cropY || 0;
|
|
@@ -157690,14 +158147,31 @@ class CropService {
|
|
|
157690
158147
|
const local = Mn.transformPoint(center, inv);
|
|
157691
158148
|
const localW = zone.getScaledWidth() / Math.abs(img.scaleX);
|
|
157692
158149
|
const localH = zone.getScaledHeight() / Math.abs(img.scaleY);
|
|
157693
|
-
|
|
157694
|
-
|
|
157695
|
-
|
|
157696
|
-
|
|
157697
|
-
|
|
157698
|
-
|
|
157699
|
-
|
|
157700
|
-
|
|
158150
|
+
if (this.maskPath) {
|
|
158151
|
+
const maskClip = new no(this.maskPath, {
|
|
158152
|
+
originX: "center",
|
|
158153
|
+
originY: "center"
|
|
158154
|
+
});
|
|
158155
|
+
const pathW = maskClip.width || 1;
|
|
158156
|
+
const pathH = maskClip.height || 1;
|
|
158157
|
+
maskClip.set({
|
|
158158
|
+
left: local.x,
|
|
158159
|
+
top: local.y,
|
|
158160
|
+
scaleX: localW / pathW,
|
|
158161
|
+
scaleY: localH / pathH,
|
|
158162
|
+
angle: 0
|
|
158163
|
+
});
|
|
158164
|
+
img.clipPath = maskClip;
|
|
158165
|
+
} else {
|
|
158166
|
+
img.clipPath = new jr({
|
|
158167
|
+
left: local.x,
|
|
158168
|
+
top: local.y,
|
|
158169
|
+
width: localW,
|
|
158170
|
+
height: localH,
|
|
158171
|
+
originX: "center",
|
|
158172
|
+
originY: "center"
|
|
158173
|
+
});
|
|
158174
|
+
}
|
|
157701
158175
|
img.dirty = true;
|
|
157702
158176
|
}
|
|
157703
158177
|
/* ======================= APPLY CROP ======================= */
|
|
@@ -157722,11 +158196,13 @@ class CropService {
|
|
|
157722
158196
|
const minY = Math.min(...ys2);
|
|
157723
158197
|
const maxX = Math.max(...xs2);
|
|
157724
158198
|
const maxY = Math.max(...ys2);
|
|
158199
|
+
const newW = Math.min(full.width, maxX - minX);
|
|
158200
|
+
const newH = Math.min(full.height, maxY - minY);
|
|
157725
158201
|
img.set({
|
|
157726
158202
|
cropX: Math.max(0, minX),
|
|
157727
158203
|
cropY: Math.max(0, minY),
|
|
157728
|
-
width:
|
|
157729
|
-
height:
|
|
158204
|
+
width: newW,
|
|
158205
|
+
height: newH,
|
|
157730
158206
|
scaleX: full.scaleX,
|
|
157731
158207
|
scaleY: full.scaleY,
|
|
157732
158208
|
angle: full.angle,
|
|
@@ -157738,6 +158214,35 @@ class CropService {
|
|
|
157738
158214
|
selectable: true,
|
|
157739
158215
|
evented: true
|
|
157740
158216
|
});
|
|
158217
|
+
if (this.maskPath) {
|
|
158218
|
+
const newClip = new no(this.maskPath, {
|
|
158219
|
+
originX: "center",
|
|
158220
|
+
originY: "center"
|
|
158221
|
+
});
|
|
158222
|
+
const pathW = newClip.width || 1;
|
|
158223
|
+
const pathH = newClip.height || 1;
|
|
158224
|
+
newClip.set({
|
|
158225
|
+
scaleX: newW / pathW,
|
|
158226
|
+
scaleY: newH / pathH,
|
|
158227
|
+
left: 0,
|
|
158228
|
+
top: 0,
|
|
158229
|
+
angle: 0
|
|
158230
|
+
});
|
|
158231
|
+
newClip.__maskPath = this.maskPath;
|
|
158232
|
+
img.__maskPath = this.maskPath;
|
|
158233
|
+
img.set({ clipPath: newClip });
|
|
158234
|
+
} else if (img.clipPath && img.clipPath.type === "path") {
|
|
158235
|
+
const pW = img.clipPath.width || 1;
|
|
158236
|
+
const pH = img.clipPath.height || 1;
|
|
158237
|
+
img.clipPath.set({
|
|
158238
|
+
scaleX: newW / pW,
|
|
158239
|
+
scaleY: newH / pH,
|
|
158240
|
+
left: 0,
|
|
158241
|
+
top: 0,
|
|
158242
|
+
originX: "center",
|
|
158243
|
+
originY: "center"
|
|
158244
|
+
});
|
|
158245
|
+
}
|
|
157741
158246
|
img.setPositionByOrigin(
|
|
157742
158247
|
new ot(zone.left, zone.top),
|
|
157743
158248
|
"center",
|
|
@@ -157746,7 +158251,9 @@ class CropService {
|
|
|
157746
158251
|
img.setCoords();
|
|
157747
158252
|
this.cleanup();
|
|
157748
158253
|
this.canvas.setActiveObject(img);
|
|
158254
|
+
bringOverlaysToFront(this.canvas);
|
|
157749
158255
|
this.canvas.requestRenderAll();
|
|
158256
|
+
useHistoryStore.getState().saveState();
|
|
157750
158257
|
this.onEnd?.();
|
|
157751
158258
|
}
|
|
157752
158259
|
/* ======================== CANCEL ========================== */
|
|
@@ -157782,6 +158289,7 @@ class CropService {
|
|
|
157782
158289
|
this.fullImage = null;
|
|
157783
158290
|
this.darkOverlay = null;
|
|
157784
158291
|
this.targetImage = null;
|
|
158292
|
+
this.maskPath = null;
|
|
157785
158293
|
}
|
|
157786
158294
|
}
|
|
157787
158295
|
const cropService = new CropService();
|
|
@@ -157868,7 +158376,7 @@ function PictureToolbar() {
|
|
|
157868
158376
|
const [isRemovingBg, setIsRemovingBg] = useState(false);
|
|
157869
158377
|
const [bgStatus, setBgStatus] = useState(null);
|
|
157870
158378
|
const activeObj = canvas2?.getActiveObject();
|
|
157871
|
-
const hasMask = !!activeObj?.clipPath;
|
|
158379
|
+
const hasMask = !!activeObj?.clipPath || !!activeObj?.__maskPath;
|
|
157872
158380
|
activeObj && (activeObj.type === "image" || activeObj.isType?.("image"));
|
|
157873
158381
|
const isBgRemoved = !!activeObj?.__originalSrc || !!activeObj?.__isBgRemoved;
|
|
157874
158382
|
if (type !== "image" || objects?.[0]?.name === "qr" || objects?.[0]?.name === "barcode")
|
|
@@ -157877,8 +158385,9 @@ function PictureToolbar() {
|
|
|
157877
158385
|
if (!canvas2) return;
|
|
157878
158386
|
const obj = canvas2.getActiveObject();
|
|
157879
158387
|
if (!obj) return;
|
|
157880
|
-
if (obj.clipPath) {
|
|
158388
|
+
if (obj.clipPath || obj.__maskPath) {
|
|
157881
158389
|
obj.set({ clipPath: null });
|
|
158390
|
+
obj.__maskPath = null;
|
|
157882
158391
|
obj.setCoords();
|
|
157883
158392
|
canvas2.requestRenderAll();
|
|
157884
158393
|
saveState();
|
|
@@ -158149,6 +158658,20 @@ function DuplicateButton() {
|
|
|
158149
158658
|
const duplicate = async () => {
|
|
158150
158659
|
const active = canvas2.getActiveObject();
|
|
158151
158660
|
if (!active) return;
|
|
158661
|
+
const currentSide = useHistoryStore.getState().currentPage;
|
|
158662
|
+
const ensureTextLoaded = async (obj) => {
|
|
158663
|
+
if (obj.type === "textbox" || obj.type === "text" || obj.type === "i-text") {
|
|
158664
|
+
if (obj.fontFamily) {
|
|
158665
|
+
try {
|
|
158666
|
+
await document.fonts.load(`16px "${obj.fontFamily}"`);
|
|
158667
|
+
} catch {
|
|
158668
|
+
}
|
|
158669
|
+
}
|
|
158670
|
+
obj.initDimensions?.();
|
|
158671
|
+
obj.set("dirty", true);
|
|
158672
|
+
}
|
|
158673
|
+
obj.__pageSide = currentSide;
|
|
158674
|
+
};
|
|
158152
158675
|
if (active.type?.toLowerCase() === "activeselection") {
|
|
158153
158676
|
const selection = active;
|
|
158154
158677
|
const originals = selection.getObjects().filter((o2) => o2.name !== "workarea");
|
|
@@ -158156,35 +158679,41 @@ function DuplicateButton() {
|
|
|
158156
158679
|
canvas2.discardActiveObject();
|
|
158157
158680
|
const clones = await Promise.all(
|
|
158158
158681
|
originals.map(async (obj) => {
|
|
158159
|
-
const cloned2 = await obj.clone();
|
|
158682
|
+
const cloned2 = await obj.clone(HISTORY_CUSTOM_PROPERTIES);
|
|
158160
158683
|
cloned2.set({
|
|
158161
158684
|
left: (obj.left ?? 0) + OFFSET$1,
|
|
158162
158685
|
top: (obj.top ?? 0) + OFFSET$1,
|
|
158163
158686
|
selectable: true,
|
|
158164
158687
|
evented: true
|
|
158165
158688
|
});
|
|
158689
|
+
await ensureTextLoaded(cloned2);
|
|
158166
158690
|
canvas2.add(cloned2);
|
|
158167
158691
|
return cloned2;
|
|
158168
158692
|
})
|
|
158169
158693
|
);
|
|
158170
158694
|
const newSelection = clones.length > 1 ? new Qo(clones, { canvas: canvas2 }) : clones[0];
|
|
158171
158695
|
canvas2.setActiveObject(newSelection);
|
|
158696
|
+
bringOverlaysToFront(canvas2);
|
|
158172
158697
|
canvas2.requestRenderAll();
|
|
158173
158698
|
saveState();
|
|
158174
158699
|
return;
|
|
158175
158700
|
}
|
|
158176
158701
|
if (active.name === "workarea") return;
|
|
158177
|
-
const cloned = await active.clone();
|
|
158702
|
+
const cloned = await active.clone(HISTORY_CUSTOM_PROPERTIES);
|
|
158178
158703
|
cloned.set({
|
|
158179
158704
|
left: (active.left ?? 0) + OFFSET$1,
|
|
158180
158705
|
top: (active.top ?? 0) + OFFSET$1,
|
|
158181
158706
|
selectable: true,
|
|
158182
158707
|
evented: true
|
|
158183
158708
|
});
|
|
158709
|
+
await ensureTextLoaded(cloned);
|
|
158710
|
+
cloned.setCoords();
|
|
158184
158711
|
canvas2.discardActiveObject();
|
|
158185
158712
|
canvas2.add(cloned);
|
|
158186
158713
|
canvas2.setActiveObject(cloned);
|
|
158714
|
+
bringOverlaysToFront(canvas2);
|
|
158187
158715
|
canvas2.requestRenderAll();
|
|
158716
|
+
saveState();
|
|
158188
158717
|
};
|
|
158189
158718
|
return /* @__PURE__ */ jsx(
|
|
158190
158719
|
"button",
|
|
@@ -158737,44 +159266,50 @@ function PositionPicker() {
|
|
|
158737
159266
|
if (!active) return [];
|
|
158738
159267
|
return active.type?.toLowerCase() === "activeselection" ? active.getObjects() : [active];
|
|
158739
159268
|
};
|
|
158740
|
-
const
|
|
158741
|
-
const normalizeOrigin = (obj) => {
|
|
158742
|
-
if (obj.originX === "left" && obj.originY === "top") return;
|
|
158743
|
-
const point = obj.getPointByOrigin("left", "top");
|
|
158744
|
-
obj.set({ originX: "left", originY: "top" });
|
|
158745
|
-
obj.setPositionByOrigin(point, "left", "top");
|
|
158746
|
-
};
|
|
159269
|
+
const getAlignmentBoundary = () => canvas2.getObjects().find((o2) => o2.name === "workarea");
|
|
158747
159270
|
const applyAlign = (align) => {
|
|
158748
|
-
const
|
|
158749
|
-
if (!
|
|
158750
|
-
const
|
|
158751
|
-
const
|
|
158752
|
-
const
|
|
158753
|
-
const
|
|
159271
|
+
const boundary = getAlignmentBoundary();
|
|
159272
|
+
if (!boundary) return;
|
|
159273
|
+
const bBounds = boundary.getBoundingRect();
|
|
159274
|
+
const bLeft = bBounds.left;
|
|
159275
|
+
const bTop = bBounds.top;
|
|
159276
|
+
const bWidth = bBounds.width;
|
|
159277
|
+
const bHeight = bBounds.height;
|
|
158754
159278
|
getTargets().forEach((obj) => {
|
|
158755
|
-
|
|
158756
|
-
const
|
|
158757
|
-
const
|
|
159279
|
+
const objBounds = obj.getBoundingRect();
|
|
159280
|
+
const pad = obj.padding || 0;
|
|
159281
|
+
const effLeft = objBounds.left - pad;
|
|
159282
|
+
const effTop = objBounds.top - pad;
|
|
159283
|
+
const effWidth = objBounds.width + pad * 2;
|
|
159284
|
+
const effHeight = objBounds.height + pad * 2;
|
|
159285
|
+
let dx = 0;
|
|
159286
|
+
let dy = 0;
|
|
158758
159287
|
switch (align) {
|
|
158759
159288
|
case "left":
|
|
158760
|
-
|
|
159289
|
+
dx = bLeft - effLeft;
|
|
158761
159290
|
break;
|
|
158762
159291
|
case "center":
|
|
158763
|
-
|
|
159292
|
+
dx = bLeft + (bWidth - effWidth) / 2 - effLeft;
|
|
158764
159293
|
break;
|
|
158765
159294
|
case "right":
|
|
158766
|
-
|
|
159295
|
+
dx = bLeft + bWidth - effWidth - effLeft;
|
|
158767
159296
|
break;
|
|
158768
159297
|
case "top":
|
|
158769
|
-
|
|
159298
|
+
dy = bTop - effTop;
|
|
158770
159299
|
break;
|
|
158771
159300
|
case "middle":
|
|
158772
|
-
|
|
159301
|
+
dy = bTop + (bHeight - effHeight) / 2 - effTop;
|
|
158773
159302
|
break;
|
|
158774
159303
|
case "bottom":
|
|
158775
|
-
|
|
159304
|
+
dy = bTop + bHeight - effHeight - effTop;
|
|
158776
159305
|
break;
|
|
158777
159306
|
}
|
|
159307
|
+
if (dx !== 0) {
|
|
159308
|
+
obj.set({ left: (obj.left ?? 0) + dx });
|
|
159309
|
+
}
|
|
159310
|
+
if (dy !== 0) {
|
|
159311
|
+
obj.set({ top: (obj.top ?? 0) + dy });
|
|
159312
|
+
}
|
|
158778
159313
|
obj.setCoords();
|
|
158779
159314
|
});
|
|
158780
159315
|
canvas2.requestRenderAll();
|
|
@@ -158797,42 +159332,40 @@ function PositionPicker() {
|
|
|
158797
159332
|
const objects = active.getObjects();
|
|
158798
159333
|
if (objects.length && objects.length > 1) {
|
|
158799
159334
|
objects.forEach(function(obj) {
|
|
158800
|
-
const itemWidth = obj.
|
|
159335
|
+
const itemWidth = obj.getScaledWidth();
|
|
159336
|
+
const itemHeight = obj.getScaledHeight();
|
|
159337
|
+
obj.set({ originX: "center", originY: "center" });
|
|
158801
159338
|
switch (align) {
|
|
158802
159339
|
case "left":
|
|
158803
|
-
obj.set({ left: -
|
|
159340
|
+
obj.set({ left: -groupWidth / 2 + itemWidth / 2 });
|
|
158804
159341
|
break;
|
|
158805
159342
|
case "center":
|
|
158806
|
-
obj.set({ left: 0
|
|
159343
|
+
obj.set({ left: 0 });
|
|
158807
159344
|
break;
|
|
158808
159345
|
case "right":
|
|
158809
|
-
obj.set({
|
|
158810
|
-
left: groupWidth / 2 - itemWidth / 2,
|
|
158811
|
-
originX: "center"
|
|
158812
|
-
});
|
|
159346
|
+
obj.set({ left: groupWidth / 2 - itemWidth / 2 });
|
|
158813
159347
|
break;
|
|
158814
159348
|
case "top":
|
|
158815
|
-
obj.set({
|
|
158816
|
-
break;
|
|
158817
|
-
case "bottom":
|
|
158818
|
-
obj.set({ originY: "bottom", top: groupHeight / 2 });
|
|
159349
|
+
obj.set({ top: -groupHeight / 2 + itemHeight / 2 });
|
|
158819
159350
|
break;
|
|
158820
159351
|
case "middle":
|
|
158821
|
-
obj.set({
|
|
158822
|
-
|
|
158823
|
-
|
|
158824
|
-
});
|
|
159352
|
+
obj.set({ top: 0 });
|
|
159353
|
+
break;
|
|
159354
|
+
case "bottom":
|
|
159355
|
+
obj.set({ top: groupHeight / 2 - itemHeight / 2 });
|
|
158825
159356
|
break;
|
|
158826
159357
|
}
|
|
158827
159358
|
obj.setCoords();
|
|
158828
|
-
canvas2.requestRenderAll();
|
|
158829
|
-
saveState();
|
|
158830
159359
|
});
|
|
159360
|
+
active.setCoords();
|
|
159361
|
+
canvas2.requestRenderAll();
|
|
159362
|
+
saveState();
|
|
158831
159363
|
}
|
|
158832
159364
|
};
|
|
158833
159365
|
const bringForward = () => {
|
|
158834
159366
|
const objects = canvas2.getObjects();
|
|
158835
159367
|
[...getTargets()].sort((a2, b2) => objects.indexOf(a2) - objects.indexOf(b2)).reverse().forEach((obj) => canvas2.bringObjectForward(obj, true));
|
|
159368
|
+
bringOverlaysToFront(canvas2);
|
|
158836
159369
|
canvas2.requestRenderAll();
|
|
158837
159370
|
saveState();
|
|
158838
159371
|
};
|
|
@@ -158861,6 +159394,7 @@ function PositionPicker() {
|
|
|
158861
159394
|
const bringToFront = () => {
|
|
158862
159395
|
const objects = canvas2.getObjects();
|
|
158863
159396
|
[...getTargets()].sort((a2, b2) => objects.indexOf(a2) - objects.indexOf(b2)).reverse().forEach((obj) => canvas2.bringObjectToFront(obj));
|
|
159397
|
+
bringOverlaysToFront(canvas2);
|
|
158864
159398
|
canvas2.requestRenderAll();
|
|
158865
159399
|
saveState();
|
|
158866
159400
|
};
|
|
@@ -159215,6 +159749,7 @@ function GroupButton() {
|
|
|
159215
159749
|
canvas2.add(group);
|
|
159216
159750
|
}
|
|
159217
159751
|
canvas2.setActiveObject(group);
|
|
159752
|
+
bringOverlaysToFront(canvas2);
|
|
159218
159753
|
canvas2.requestRenderAll();
|
|
159219
159754
|
useSelectionStore.getState().setSelection([group]);
|
|
159220
159755
|
saveState();
|
|
@@ -159366,6 +159901,9 @@ function configureObjectControls(obj) {
|
|
|
159366
159901
|
obj.__rotationControlSize = rotationSize;
|
|
159367
159902
|
obj.controls.mtr = createRotateControl(rotationSize, rotationOffset);
|
|
159368
159903
|
const textObject = isTextObject(obj);
|
|
159904
|
+
if (textObject) {
|
|
159905
|
+
obj.set({ padding: 0 });
|
|
159906
|
+
}
|
|
159369
159907
|
if (!obj.controls.mb) {
|
|
159370
159908
|
obj.controls.mb = new ai({
|
|
159371
159909
|
x: 0,
|
|
@@ -159484,6 +160022,7 @@ function UngroupButton() {
|
|
|
159484
160022
|
});
|
|
159485
160023
|
canvas2.discardActiveObject();
|
|
159486
160024
|
useSelectionStore.getState().clearSelection();
|
|
160025
|
+
bringOverlaysToFront(canvas2);
|
|
159487
160026
|
canvas2.requestRenderAll();
|
|
159488
160027
|
saveState();
|
|
159489
160028
|
};
|
|
@@ -159593,12 +160132,31 @@ function Toolbar() {
|
|
|
159593
160132
|
) });
|
|
159594
160133
|
}
|
|
159595
160134
|
function applyOpacity(hex, opacity) {
|
|
159596
|
-
const alpha = Math.max(0, Math.min(1, opacity / 100));
|
|
159597
|
-
|
|
159598
|
-
|
|
159599
|
-
|
|
159600
|
-
|
|
159601
|
-
|
|
160135
|
+
const alpha = Math.max(0, Math.min(1, (opacity ?? 100) / 100));
|
|
160136
|
+
if (!hex) return `rgba(0, 0, 0, ${alpha})`;
|
|
160137
|
+
let clean = hex.trim();
|
|
160138
|
+
if (clean.startsWith("#")) {
|
|
160139
|
+
clean = clean.slice(1);
|
|
160140
|
+
if (clean.length === 3) {
|
|
160141
|
+
clean = clean.split("").map((c2) => c2 + c2).join("");
|
|
160142
|
+
}
|
|
160143
|
+
if (clean.length >= 6) {
|
|
160144
|
+
const r2 = parseInt(clean.slice(0, 2), 16) || 0;
|
|
160145
|
+
const g2 = parseInt(clean.slice(2, 4), 16) || 0;
|
|
160146
|
+
const b2 = parseInt(clean.slice(4, 6), 16) || 0;
|
|
160147
|
+
return `rgba(${r2}, ${g2}, ${b2}, ${alpha})`;
|
|
160148
|
+
}
|
|
160149
|
+
}
|
|
160150
|
+
return clean;
|
|
160151
|
+
}
|
|
160152
|
+
const PENCIL_CURSOR_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
|
160153
|
+
<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" fill="#ffffff" stroke="#ffffff" stroke-width="2.5" stroke-linejoin="round"/>
|
|
160154
|
+
<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" fill="#1e293b" stroke="#0f172a" stroke-width="1.2" stroke-linejoin="round"/>
|
|
160155
|
+
<polygon points="2,22 5.5,20 4,18.5" fill="#f59e0b"/>
|
|
160156
|
+
<polygon points="2,22 3.2,21.3 2.7,20.8" fill="#000000"/>
|
|
160157
|
+
<path d="m15 5 4 4" stroke="#94a3b8" stroke-width="1.2" stroke-linecap="round"/>
|
|
160158
|
+
</svg>`;
|
|
160159
|
+
const PENCIL_CURSOR = `url("data:image/svg+xml;utf8,${encodeURIComponent(PENCIL_CURSOR_SVG)}") 2 22, crosshair`;
|
|
159602
160160
|
function useFabricDrawing() {
|
|
159603
160161
|
const { drawTool, strokeWidth, opacity, color } = useDrawStore();
|
|
159604
160162
|
useEffect(() => {
|
|
@@ -159608,19 +160166,99 @@ function useFabricDrawing() {
|
|
|
159608
160166
|
canvas2.isDrawingMode = false;
|
|
159609
160167
|
canvas2.selection = true;
|
|
159610
160168
|
canvas2.defaultCursor = "default";
|
|
160169
|
+
canvas2.freeDrawingCursor = "default";
|
|
160170
|
+
canvas2.setCursor("default");
|
|
160171
|
+
if (canvas2.upperCanvasEl) {
|
|
160172
|
+
canvas2.upperCanvasEl.style.cursor = "default";
|
|
160173
|
+
}
|
|
160174
|
+
canvas2._isCurrentlyDrawing = false;
|
|
159611
160175
|
return;
|
|
159612
160176
|
}
|
|
160177
|
+
if (canvas2.getActiveObject()) {
|
|
160178
|
+
canvas2.discardActiveObject();
|
|
160179
|
+
useSelectionStore.getState().clearSelection();
|
|
160180
|
+
canvas2.requestRenderAll();
|
|
160181
|
+
}
|
|
160182
|
+
const activeCursor = drawTool === "brush" ? PENCIL_CURSOR : "crosshair";
|
|
159613
160183
|
canvas2.isDrawingMode = true;
|
|
159614
160184
|
canvas2.selection = false;
|
|
159615
|
-
canvas2.defaultCursor =
|
|
160185
|
+
canvas2.defaultCursor = activeCursor;
|
|
160186
|
+
canvas2.freeDrawingCursor = activeCursor;
|
|
160187
|
+
canvas2.setCursor(activeCursor);
|
|
160188
|
+
if (canvas2.upperCanvasEl) {
|
|
160189
|
+
canvas2.upperCanvasEl.style.cursor = activeCursor;
|
|
160190
|
+
}
|
|
159616
160191
|
const brush = new oo(canvas2);
|
|
159617
|
-
brush.width = strokeWidth;
|
|
159618
|
-
brush.color = applyOpacity(color, opacity);
|
|
159619
160192
|
if (drawTool === "highlighter") {
|
|
159620
160193
|
brush.width = strokeWidth * 1.5;
|
|
159621
|
-
brush.color = applyOpacity(color,
|
|
160194
|
+
brush.color = applyOpacity(color, opacity ?? 50);
|
|
160195
|
+
} else {
|
|
160196
|
+
brush.width = strokeWidth;
|
|
160197
|
+
brush.color = applyOpacity(color, opacity ?? 100);
|
|
159622
160198
|
}
|
|
159623
160199
|
canvas2.freeDrawingBrush = brush;
|
|
160200
|
+
const handleMouseDownBefore = (opt) => {
|
|
160201
|
+
if (!canvas2.isDrawingMode) return;
|
|
160202
|
+
const target = opt.target || canvas2.findTarget(opt.e);
|
|
160203
|
+
if (!target) return;
|
|
160204
|
+
const name = target.name || "";
|
|
160205
|
+
if (name === "workarea" || name === "watermark" || name.startsWith("overlay-") || name.startsWith("guideline-")) {
|
|
160206
|
+
return;
|
|
160207
|
+
}
|
|
160208
|
+
if (target.isDrawingPath || name === "drawing-path") {
|
|
160209
|
+
return;
|
|
160210
|
+
}
|
|
160211
|
+
if (target.selectable !== false && target.evented !== false) {
|
|
160212
|
+
canvas2.isDrawingMode = false;
|
|
160213
|
+
canvas2.selection = true;
|
|
160214
|
+
canvas2.defaultCursor = "default";
|
|
160215
|
+
canvas2.freeDrawingCursor = "default";
|
|
160216
|
+
canvas2.setCursor("default");
|
|
160217
|
+
if (canvas2.upperCanvasEl) {
|
|
160218
|
+
canvas2.upperCanvasEl.style.cursor = "default";
|
|
160219
|
+
}
|
|
160220
|
+
canvas2._isCurrentlyDrawing = false;
|
|
160221
|
+
useDrawStore.getState().setDrawTool("select");
|
|
160222
|
+
canvas2.setActiveObject(target);
|
|
160223
|
+
canvas2.requestRenderAll();
|
|
160224
|
+
}
|
|
160225
|
+
};
|
|
160226
|
+
const handlePathCreated = (e3) => {
|
|
160227
|
+
if (e3?.path) {
|
|
160228
|
+
e3.path.name = "drawing-path";
|
|
160229
|
+
e3.path.isDrawingPath = true;
|
|
160230
|
+
}
|
|
160231
|
+
};
|
|
160232
|
+
const handleSelectionChange = () => {
|
|
160233
|
+
if (useDrawStore.getState().drawTool !== "select") {
|
|
160234
|
+
useDrawStore.getState().setDrawTool("select");
|
|
160235
|
+
}
|
|
160236
|
+
if (canvas2.isDrawingMode) {
|
|
160237
|
+
canvas2.isDrawingMode = false;
|
|
160238
|
+
canvas2.selection = true;
|
|
160239
|
+
canvas2.defaultCursor = "default";
|
|
160240
|
+
canvas2.freeDrawingCursor = "default";
|
|
160241
|
+
canvas2.setCursor("default");
|
|
160242
|
+
if (canvas2.upperCanvasEl) {
|
|
160243
|
+
canvas2.upperCanvasEl.style.cursor = "default";
|
|
160244
|
+
}
|
|
160245
|
+
canvas2._isCurrentlyDrawing = false;
|
|
160246
|
+
canvas2.requestRenderAll();
|
|
160247
|
+
}
|
|
160248
|
+
};
|
|
160249
|
+
canvas2.on("mouse:down:before", handleMouseDownBefore);
|
|
160250
|
+
canvas2.on("path:created", handlePathCreated);
|
|
160251
|
+
canvas2.on("selection:created", handleSelectionChange);
|
|
160252
|
+
canvas2.on("selection:updated", handleSelectionChange);
|
|
160253
|
+
return () => {
|
|
160254
|
+
canvas2.off("mouse:down:before", handleMouseDownBefore);
|
|
160255
|
+
canvas2.off("path:created", handlePathCreated);
|
|
160256
|
+
canvas2.off("selection:created", handleSelectionChange);
|
|
160257
|
+
canvas2.off("selection:updated", handleSelectionChange);
|
|
160258
|
+
if (canvas2.upperCanvasEl) {
|
|
160259
|
+
canvas2.upperCanvasEl.style.cursor = "default";
|
|
160260
|
+
}
|
|
160261
|
+
};
|
|
159624
160262
|
}, [drawTool, strokeWidth, opacity, color]);
|
|
159625
160263
|
}
|
|
159626
160264
|
function PointerIcon() {
|
|
@@ -159651,6 +160289,89 @@ function HandIcon() {
|
|
|
159651
160289
|
/* @__PURE__ */ jsx("path", { d: "M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15" })
|
|
159652
160290
|
] });
|
|
159653
160291
|
}
|
|
160292
|
+
function GridIcon({
|
|
160293
|
+
size = 18,
|
|
160294
|
+
className = ""
|
|
160295
|
+
}) {
|
|
160296
|
+
return /* @__PURE__ */ jsxs(
|
|
160297
|
+
"svg",
|
|
160298
|
+
{
|
|
160299
|
+
width: size,
|
|
160300
|
+
height: size,
|
|
160301
|
+
viewBox: "0 0 24 24",
|
|
160302
|
+
fill: "none",
|
|
160303
|
+
stroke: "currentColor",
|
|
160304
|
+
strokeWidth: "2",
|
|
160305
|
+
strokeLinecap: "round",
|
|
160306
|
+
strokeLinejoin: "round",
|
|
160307
|
+
className,
|
|
160308
|
+
children: [
|
|
160309
|
+
/* @__PURE__ */ jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
|
|
160310
|
+
/* @__PURE__ */ jsx("path", { d: "M3 9h18" }),
|
|
160311
|
+
/* @__PURE__ */ jsx("path", { d: "M3 15h18" }),
|
|
160312
|
+
/* @__PURE__ */ jsx("path", { d: "M9 3v18" }),
|
|
160313
|
+
/* @__PURE__ */ jsx("path", { d: "M15 3v18" })
|
|
160314
|
+
]
|
|
160315
|
+
}
|
|
160316
|
+
);
|
|
160317
|
+
}
|
|
160318
|
+
function getGridStep(width, height) {
|
|
160319
|
+
const minDim = Math.min(width, height);
|
|
160320
|
+
if (minDim <= 200) return 10;
|
|
160321
|
+
if (minDim <= 400) return 20;
|
|
160322
|
+
if (minDim <= 800) return 25;
|
|
160323
|
+
return 40;
|
|
160324
|
+
}
|
|
160325
|
+
function renderGrid(canvas2) {
|
|
160326
|
+
if (!canvas2) return;
|
|
160327
|
+
canvas2.getObjects().forEach((o2) => {
|
|
160328
|
+
if (o2.name === "canvas-grid") {
|
|
160329
|
+
canvas2.remove(o2);
|
|
160330
|
+
}
|
|
160331
|
+
});
|
|
160332
|
+
const showGrid = useEditorStore.getState().showGrid;
|
|
160333
|
+
if (!showGrid) {
|
|
160334
|
+
canvas2.requestRenderAll();
|
|
160335
|
+
return;
|
|
160336
|
+
}
|
|
160337
|
+
const workarea = canvas2.getObjects().find((o2) => o2.name === "workarea");
|
|
160338
|
+
if (!workarea) {
|
|
160339
|
+
canvas2.requestRenderAll();
|
|
160340
|
+
return;
|
|
160341
|
+
}
|
|
160342
|
+
const waL = workarea.left ?? 0;
|
|
160343
|
+
const waT = workarea.top ?? 0;
|
|
160344
|
+
const waW = workarea.getScaledWidth();
|
|
160345
|
+
const waH = workarea.getScaledHeight();
|
|
160346
|
+
if (waW <= 0 || waH <= 0) return;
|
|
160347
|
+
const step = getGridStep(waW, waH);
|
|
160348
|
+
let pathData = "";
|
|
160349
|
+
for (let x2 = step; x2 < waW; x2 += step) {
|
|
160350
|
+
const roundX = Math.round(x2 * 100) / 100;
|
|
160351
|
+
pathData += `M ${roundX} 0 V ${waH} `;
|
|
160352
|
+
}
|
|
160353
|
+
for (let y2 = step; y2 < waH; y2 += step) {
|
|
160354
|
+
const roundY = Math.round(y2 * 100) / 100;
|
|
160355
|
+
pathData += `M 0 ${roundY} H ${waW} `;
|
|
160356
|
+
}
|
|
160357
|
+
if (!pathData) return;
|
|
160358
|
+
const gridPath = new no(pathData, {
|
|
160359
|
+
name: "canvas-grid",
|
|
160360
|
+
left: waL,
|
|
160361
|
+
top: waT,
|
|
160362
|
+
originX: "left",
|
|
160363
|
+
originY: "top",
|
|
160364
|
+
stroke: "rgba(100, 116, 139, 0.28)",
|
|
160365
|
+
strokeWidth: 1,
|
|
160366
|
+
fill: "",
|
|
160367
|
+
selectable: false,
|
|
160368
|
+
evented: false,
|
|
160369
|
+
excludeFromExport: true
|
|
160370
|
+
});
|
|
160371
|
+
canvas2.add(gridPath);
|
|
160372
|
+
bringOverlaysToFront(canvas2);
|
|
160373
|
+
canvas2.requestRenderAll();
|
|
160374
|
+
}
|
|
159654
160375
|
function enablePanMode(canvas2) {
|
|
159655
160376
|
let isDragging = false;
|
|
159656
160377
|
let lastClientX = 0;
|
|
@@ -159912,16 +160633,186 @@ function createWorkarea(canvas2, container, productData, pageSide) {
|
|
|
159912
160633
|
createOverlays(canvas2, workarea, currentSide);
|
|
159913
160634
|
return workarea;
|
|
159914
160635
|
}
|
|
160636
|
+
const SNAP_THRESHOLD_PX = 7;
|
|
160637
|
+
const GUIDELINE_COLOR = "#28a128";
|
|
160638
|
+
const GUIDELINE_WIDTH = 1.2;
|
|
160639
|
+
let activeVLine = null;
|
|
160640
|
+
let activeHLine = null;
|
|
160641
|
+
function clearAlignmentGuidelines(canvas2) {
|
|
160642
|
+
if (!canvas2) return;
|
|
160643
|
+
let changed = false;
|
|
160644
|
+
if (activeVLine) {
|
|
160645
|
+
canvas2.remove(activeVLine);
|
|
160646
|
+
activeVLine = null;
|
|
160647
|
+
changed = true;
|
|
160648
|
+
}
|
|
160649
|
+
if (activeHLine) {
|
|
160650
|
+
canvas2.remove(activeHLine);
|
|
160651
|
+
activeHLine = null;
|
|
160652
|
+
changed = true;
|
|
160653
|
+
}
|
|
160654
|
+
canvas2.getObjects().forEach((o2) => {
|
|
160655
|
+
if (o2.name === "guideline-alignment-v" || o2.name === "guideline-alignment-h") {
|
|
160656
|
+
canvas2.remove(o2);
|
|
160657
|
+
changed = true;
|
|
160658
|
+
}
|
|
160659
|
+
});
|
|
160660
|
+
if (changed) {
|
|
160661
|
+
canvas2.requestRenderAll();
|
|
160662
|
+
}
|
|
160663
|
+
}
|
|
160664
|
+
function handleObjectAlignmentSnap(canvas2, target) {
|
|
160665
|
+
if (!canvas2 || !target) return;
|
|
160666
|
+
const targetName = target.name || "";
|
|
160667
|
+
if (targetName === "workarea" || targetName === "bg-image" || targetName === "canvas-grid" || targetName.startsWith("overlay-") || targetName.startsWith("guideline-")) {
|
|
160668
|
+
return;
|
|
160669
|
+
}
|
|
160670
|
+
const workarea = canvas2.getObjects().find((o2) => o2.name === "workarea");
|
|
160671
|
+
if (!workarea) return;
|
|
160672
|
+
const zoom = canvas2.getZoom() || 1;
|
|
160673
|
+
const threshold = SNAP_THRESHOLD_PX / zoom;
|
|
160674
|
+
const waL = workarea.left ?? 0;
|
|
160675
|
+
const waT = workarea.top ?? 0;
|
|
160676
|
+
const waW = workarea.getScaledWidth();
|
|
160677
|
+
const waH = workarea.getScaledHeight();
|
|
160678
|
+
const waCenterX = waL + waW / 2;
|
|
160679
|
+
const waCenterY = waT + waH / 2;
|
|
160680
|
+
const targetCenter = target.getCenterPoint();
|
|
160681
|
+
target.getBoundingRect();
|
|
160682
|
+
const curLeft = target.left ?? 0;
|
|
160683
|
+
target.top ?? 0;
|
|
160684
|
+
let snappedX = false;
|
|
160685
|
+
let snappedY = false;
|
|
160686
|
+
let snapLineX = waCenterX;
|
|
160687
|
+
let snapLineY = waCenterY;
|
|
160688
|
+
const distCenterX = Math.abs(targetCenter.x - waCenterX);
|
|
160689
|
+
if (distCenterX <= threshold) {
|
|
160690
|
+
const diffX = waCenterX - targetCenter.x;
|
|
160691
|
+
target.set("left", curLeft + diffX);
|
|
160692
|
+
target.setCoords();
|
|
160693
|
+
snappedX = true;
|
|
160694
|
+
snapLineX = waCenterX;
|
|
160695
|
+
} else {
|
|
160696
|
+
const otherObjects = canvas2.getObjects().filter(
|
|
160697
|
+
(o2) => o2 !== target && o2.selectable !== false && o2.name !== "workarea" && o2.name !== "bg-image" && !o2.name?.startsWith("overlay-") && !o2.name?.startsWith("guideline-") && o2.name !== "canvas-grid"
|
|
160698
|
+
);
|
|
160699
|
+
for (const other of otherObjects) {
|
|
160700
|
+
const otherCenter = other.getCenterPoint();
|
|
160701
|
+
if (Math.abs(targetCenter.x - otherCenter.x) <= threshold) {
|
|
160702
|
+
const diffX = otherCenter.x - targetCenter.x;
|
|
160703
|
+
target.set("left", curLeft + diffX);
|
|
160704
|
+
target.setCoords();
|
|
160705
|
+
snappedX = true;
|
|
160706
|
+
snapLineX = otherCenter.x;
|
|
160707
|
+
break;
|
|
160708
|
+
}
|
|
160709
|
+
}
|
|
160710
|
+
}
|
|
160711
|
+
const updatedCenter = target.getCenterPoint();
|
|
160712
|
+
const distCenterY = Math.abs(updatedCenter.y - waCenterY);
|
|
160713
|
+
if (distCenterY <= threshold) {
|
|
160714
|
+
const diffY = waCenterY - updatedCenter.y;
|
|
160715
|
+
target.set("top", (target.top ?? 0) + diffY);
|
|
160716
|
+
target.setCoords();
|
|
160717
|
+
snappedY = true;
|
|
160718
|
+
snapLineY = waCenterY;
|
|
160719
|
+
} else {
|
|
160720
|
+
const otherObjects = canvas2.getObjects().filter(
|
|
160721
|
+
(o2) => o2 !== target && o2.selectable !== false && o2.name !== "workarea" && o2.name !== "bg-image" && !o2.name?.startsWith("overlay-") && !o2.name?.startsWith("guideline-") && o2.name !== "canvas-grid"
|
|
160722
|
+
);
|
|
160723
|
+
for (const other of otherObjects) {
|
|
160724
|
+
const otherCenter = other.getCenterPoint();
|
|
160725
|
+
if (Math.abs(updatedCenter.y - otherCenter.y) <= threshold) {
|
|
160726
|
+
const diffY = otherCenter.y - updatedCenter.y;
|
|
160727
|
+
target.set("top", (target.top ?? 0) + diffY);
|
|
160728
|
+
target.setCoords();
|
|
160729
|
+
snappedY = true;
|
|
160730
|
+
snapLineY = otherCenter.y;
|
|
160731
|
+
break;
|
|
160732
|
+
}
|
|
160733
|
+
}
|
|
160734
|
+
}
|
|
160735
|
+
if (snappedX) {
|
|
160736
|
+
const y1 = waT;
|
|
160737
|
+
const y2 = waT + waH;
|
|
160738
|
+
if (!activeVLine) {
|
|
160739
|
+
activeVLine = new vo([snapLineX, y1, snapLineX, y2], {
|
|
160740
|
+
name: "guideline-alignment-v",
|
|
160741
|
+
stroke: GUIDELINE_COLOR,
|
|
160742
|
+
strokeWidth: GUIDELINE_WIDTH / zoom,
|
|
160743
|
+
strokeUniform: true,
|
|
160744
|
+
selectable: false,
|
|
160745
|
+
evented: false,
|
|
160746
|
+
excludeFromExport: true,
|
|
160747
|
+
hasControls: false,
|
|
160748
|
+
hasBorders: false,
|
|
160749
|
+
lockMovementX: true,
|
|
160750
|
+
lockMovementY: true
|
|
160751
|
+
});
|
|
160752
|
+
canvas2.add(activeVLine);
|
|
160753
|
+
} else {
|
|
160754
|
+
activeVLine.set({
|
|
160755
|
+
x1: snapLineX,
|
|
160756
|
+
y1,
|
|
160757
|
+
x2: snapLineX,
|
|
160758
|
+
y2,
|
|
160759
|
+
strokeWidth: GUIDELINE_WIDTH / zoom,
|
|
160760
|
+
visible: true
|
|
160761
|
+
});
|
|
160762
|
+
activeVLine.setCoords();
|
|
160763
|
+
}
|
|
160764
|
+
canvas2.bringObjectToFront(activeVLine);
|
|
160765
|
+
} else if (activeVLine) {
|
|
160766
|
+
canvas2.remove(activeVLine);
|
|
160767
|
+
activeVLine = null;
|
|
160768
|
+
}
|
|
160769
|
+
if (snappedY) {
|
|
160770
|
+
const x1 = waL;
|
|
160771
|
+
const x2 = waL + waW;
|
|
160772
|
+
if (!activeHLine) {
|
|
160773
|
+
activeHLine = new vo([x1, snapLineY, x2, snapLineY], {
|
|
160774
|
+
name: "guideline-alignment-h",
|
|
160775
|
+
stroke: GUIDELINE_COLOR,
|
|
160776
|
+
strokeWidth: GUIDELINE_WIDTH / zoom,
|
|
160777
|
+
strokeUniform: true,
|
|
160778
|
+
selectable: false,
|
|
160779
|
+
evented: false,
|
|
160780
|
+
excludeFromExport: true,
|
|
160781
|
+
hasControls: false,
|
|
160782
|
+
hasBorders: false,
|
|
160783
|
+
lockMovementX: true,
|
|
160784
|
+
lockMovementY: true
|
|
160785
|
+
});
|
|
160786
|
+
canvas2.add(activeHLine);
|
|
160787
|
+
} else {
|
|
160788
|
+
activeHLine.set({
|
|
160789
|
+
x1,
|
|
160790
|
+
y1: snapLineY,
|
|
160791
|
+
x2,
|
|
160792
|
+
y2: snapLineY,
|
|
160793
|
+
strokeWidth: GUIDELINE_WIDTH / zoom,
|
|
160794
|
+
visible: true
|
|
160795
|
+
});
|
|
160796
|
+
activeHLine.setCoords();
|
|
160797
|
+
}
|
|
160798
|
+
canvas2.bringObjectToFront(activeHLine);
|
|
160799
|
+
} else if (activeHLine) {
|
|
160800
|
+
canvas2.remove(activeHLine);
|
|
160801
|
+
activeHLine = null;
|
|
160802
|
+
}
|
|
160803
|
+
canvas2.requestRenderAll();
|
|
160804
|
+
}
|
|
159915
160805
|
function setupHistoryRecording(canvas2) {
|
|
159916
160806
|
let modifiedTimer = null;
|
|
159917
160807
|
const handleObjectAddedOrRemoved = (e3) => {
|
|
159918
160808
|
const target = e3?.target;
|
|
159919
160809
|
if (!target) return;
|
|
159920
160810
|
const name = target.name || "";
|
|
159921
|
-
if (name === "workarea" || name === "watermark" || name.startsWith("overlay-") || name.startsWith("guideline-")) {
|
|
160811
|
+
if (name === "workarea" || name === "watermark" || name === "bg-image" || name.startsWith("overlay-") || name.startsWith("guideline-") || name === "canvas-grid") {
|
|
159922
160812
|
return;
|
|
159923
160813
|
}
|
|
159924
160814
|
if (useHistoryStore.getState().isRestoring) return;
|
|
160815
|
+
flushBackgroundSave();
|
|
159925
160816
|
if (modifiedTimer) {
|
|
159926
160817
|
clearTimeout(modifiedTimer);
|
|
159927
160818
|
modifiedTimer = null;
|
|
@@ -159936,7 +160827,7 @@ function setupHistoryRecording(canvas2) {
|
|
|
159936
160827
|
const target = e3?.target;
|
|
159937
160828
|
if (!target) return;
|
|
159938
160829
|
const name = target.name || "";
|
|
159939
|
-
if (name === "workarea" || name === "watermark" || name.startsWith("overlay-") || name.startsWith("guideline-")) {
|
|
160830
|
+
if (name === "workarea" || name === "watermark" || name.startsWith("overlay-") || name.startsWith("guideline-") || name === "canvas-grid") {
|
|
159940
160831
|
return;
|
|
159941
160832
|
}
|
|
159942
160833
|
if (useHistoryStore.getState().isRestoring) return;
|
|
@@ -159959,6 +160850,12 @@ function setupHistoryRecording(canvas2) {
|
|
|
159959
160850
|
}
|
|
159960
160851
|
function setupDoublClickCrop(canvas2) {
|
|
159961
160852
|
const handleDoubleClick = (e3) => {
|
|
160853
|
+
const isCropping = useEditorStore.getState().isCropping;
|
|
160854
|
+
if (isCropping) {
|
|
160855
|
+
applyCrop(canvas2);
|
|
160856
|
+
useEditorStore.getState().stopCropping();
|
|
160857
|
+
return;
|
|
160858
|
+
}
|
|
159962
160859
|
const target = e3.target;
|
|
159963
160860
|
if (target && target.type === "image" && target.name !== "bg-image") {
|
|
159964
160861
|
useEditorStore.getState().startCropping();
|
|
@@ -160004,6 +160901,13 @@ function setupSelectionHandlers(canvas2, setSelection, clearSelection) {
|
|
|
160004
160901
|
canvas2.requestRenderAll();
|
|
160005
160902
|
};
|
|
160006
160903
|
canvas2.on("selection:created", (e3) => {
|
|
160904
|
+
if (useDrawStore.getState().drawTool !== "select") {
|
|
160905
|
+
useDrawStore.getState().setDrawTool("select");
|
|
160906
|
+
canvas2.isDrawingMode = false;
|
|
160907
|
+
canvas2.selection = true;
|
|
160908
|
+
canvas2.defaultCursor = "default";
|
|
160909
|
+
canvas2._isCurrentlyDrawing = false;
|
|
160910
|
+
}
|
|
160007
160911
|
if (e3.selected) {
|
|
160008
160912
|
e3.selected.forEach((o2) => {
|
|
160009
160913
|
o2.__showDegree = false;
|
|
@@ -160011,8 +160915,16 @@ function setupSelectionHandlers(canvas2, setSelection, clearSelection) {
|
|
|
160011
160915
|
}
|
|
160012
160916
|
updateSelection();
|
|
160013
160917
|
configureSelectionControls();
|
|
160918
|
+
bringOverlaysToFront(canvas2);
|
|
160014
160919
|
});
|
|
160015
160920
|
canvas2.on("selection:updated", (e3) => {
|
|
160921
|
+
if (useDrawStore.getState().drawTool !== "select") {
|
|
160922
|
+
useDrawStore.getState().setDrawTool("select");
|
|
160923
|
+
canvas2.isDrawingMode = false;
|
|
160924
|
+
canvas2.selection = true;
|
|
160925
|
+
canvas2.defaultCursor = "default";
|
|
160926
|
+
canvas2._isCurrentlyDrawing = false;
|
|
160927
|
+
}
|
|
160016
160928
|
const active = canvas2.getActiveObject();
|
|
160017
160929
|
const bgImg = canvas2.getObjects().find((o2) => o2.name === "bg-image");
|
|
160018
160930
|
if (bgImg && active !== bgImg) {
|
|
@@ -160033,6 +160945,7 @@ function setupSelectionHandlers(canvas2, setSelection, clearSelection) {
|
|
|
160033
160945
|
}
|
|
160034
160946
|
updateSelection();
|
|
160035
160947
|
configureSelectionControls();
|
|
160948
|
+
bringOverlaysToFront(canvas2);
|
|
160036
160949
|
});
|
|
160037
160950
|
canvas2.on("object:scaling", configureSelectionControls);
|
|
160038
160951
|
canvas2.on("object:rotating", (e3) => {
|
|
@@ -160049,10 +160962,23 @@ function setupSelectionHandlers(canvas2, setSelection, clearSelection) {
|
|
|
160049
160962
|
o2.__showDegree = false;
|
|
160050
160963
|
});
|
|
160051
160964
|
configureSelectionControls();
|
|
160965
|
+
bringOverlaysToFront(canvas2);
|
|
160052
160966
|
};
|
|
160053
|
-
|
|
160054
|
-
|
|
160967
|
+
const handleObjectMoving = (e3) => {
|
|
160968
|
+
if (e3.target) {
|
|
160969
|
+
handleObjectAlignmentSnap(canvas2, e3.target);
|
|
160970
|
+
}
|
|
160971
|
+
bringOverlaysToFront(canvas2);
|
|
160972
|
+
};
|
|
160973
|
+
const handleMovementEnd = (e3) => {
|
|
160974
|
+
clearAlignmentGuidelines(canvas2);
|
|
160975
|
+
hideDegree(e3);
|
|
160976
|
+
};
|
|
160977
|
+
canvas2.on("object:moving", handleObjectMoving);
|
|
160978
|
+
canvas2.on("object:modified", handleMovementEnd);
|
|
160979
|
+
canvas2.on("mouse:up", handleMovementEnd);
|
|
160055
160980
|
canvas2.on("selection:cleared", () => {
|
|
160981
|
+
clearAlignmentGuidelines(canvas2);
|
|
160056
160982
|
const bgImg = canvas2.getObjects().find((o2) => o2.name === "bg-image");
|
|
160057
160983
|
if (bgImg) {
|
|
160058
160984
|
bgImg.set({
|
|
@@ -160066,22 +160992,27 @@ function setupSelectionHandlers(canvas2, setSelection, clearSelection) {
|
|
|
160066
160992
|
}
|
|
160067
160993
|
}
|
|
160068
160994
|
hideDegree();
|
|
160995
|
+
bringOverlaysToFront(canvas2);
|
|
160069
160996
|
clearSelection();
|
|
160070
160997
|
});
|
|
160071
160998
|
return () => {
|
|
160999
|
+
clearAlignmentGuidelines(canvas2);
|
|
160072
161000
|
canvas2.off("selection:created");
|
|
160073
161001
|
canvas2.off("selection:updated");
|
|
160074
161002
|
canvas2.off("object:scaling", configureSelectionControls);
|
|
160075
161003
|
canvas2.off("object:rotating");
|
|
160076
|
-
canvas2.off("object:
|
|
160077
|
-
canvas2.off("
|
|
161004
|
+
canvas2.off("object:moving", handleObjectMoving);
|
|
161005
|
+
canvas2.off("object:modified", handleMovementEnd);
|
|
161006
|
+
canvas2.off("mouse:up", handleMovementEnd);
|
|
160078
161007
|
canvas2.off("selection:cleared");
|
|
160079
161008
|
};
|
|
160080
161009
|
}
|
|
160081
161010
|
function setupObjectAddedHandler(canvas2) {
|
|
160082
161011
|
const handleObjectAdded = (e3) => {
|
|
160083
161012
|
const obj = e3.target;
|
|
160084
|
-
if (!obj || obj.name === "workarea")
|
|
161013
|
+
if (!obj || obj.name === "workarea" || obj.name?.startsWith("overlay-") || obj.name?.startsWith("guideline-")) {
|
|
161014
|
+
return;
|
|
161015
|
+
}
|
|
160085
161016
|
configureObjectControls(obj);
|
|
160086
161017
|
bringOverlaysToFront(canvas2);
|
|
160087
161018
|
};
|
|
@@ -160129,6 +161060,9 @@ const CUSTOM_PROPERTIES = [
|
|
|
160129
161060
|
"lockMovementY",
|
|
160130
161061
|
"rx",
|
|
160131
161062
|
"ry",
|
|
161063
|
+
"src",
|
|
161064
|
+
"crossOrigin",
|
|
161065
|
+
"__maskPath",
|
|
160132
161066
|
"__radiusOfCanvas",
|
|
160133
161067
|
"__shapeType",
|
|
160134
161068
|
"__pageSide",
|
|
@@ -160139,10 +161073,28 @@ const CUSTOM_PROPERTIES = [
|
|
|
160139
161073
|
"__initialZoom",
|
|
160140
161074
|
"__borderRect",
|
|
160141
161075
|
"__rotationControlSize",
|
|
160142
|
-
"__showDegree"
|
|
161076
|
+
"__showDegree",
|
|
161077
|
+
"textAlign",
|
|
161078
|
+
"fontFamily",
|
|
161079
|
+
"fontSize",
|
|
161080
|
+
"fontWeight",
|
|
161081
|
+
"fontStyle",
|
|
161082
|
+
"underline",
|
|
161083
|
+
"linethrough",
|
|
161084
|
+
"overline",
|
|
161085
|
+
"fill",
|
|
161086
|
+
"stroke",
|
|
161087
|
+
"strokeWidth",
|
|
161088
|
+
"lineHeight",
|
|
161089
|
+
"charSpacing",
|
|
161090
|
+
"styles",
|
|
161091
|
+
"paintFirst",
|
|
161092
|
+
"strokeUniform",
|
|
161093
|
+
"objectCaching"
|
|
160143
161094
|
];
|
|
160144
161095
|
async function switchCanvasPage(canvas2, currentPage, targetPage, pageJSON, clearSelection, containerRef) {
|
|
160145
161096
|
if (targetPage === currentPage) return;
|
|
161097
|
+
clearAlignmentGuidelines(canvas2);
|
|
160146
161098
|
const historyStore = useHistoryStore.getState();
|
|
160147
161099
|
useLoaderStore.getState().startLoading();
|
|
160148
161100
|
try {
|
|
@@ -160575,41 +161527,75 @@ function ZoomControls({ onReset }) {
|
|
|
160575
161527
|
] }) });
|
|
160576
161528
|
}
|
|
160577
161529
|
const OFFSET = 20;
|
|
160578
|
-
|
|
160579
|
-
|
|
161530
|
+
let globalClipboard = null;
|
|
161531
|
+
let clipboardSourcePage = null;
|
|
161532
|
+
function useKeyboardEvents(canvasParam) {
|
|
161533
|
+
const storeCanvas = useEditorStore((s2) => s2.canvas);
|
|
161534
|
+
const historyCanvas = useHistoryStore((s2) => s2.canvas);
|
|
161535
|
+
const canvas2 = canvasParam || storeCanvas || historyCanvas;
|
|
160580
161536
|
const saveState = useHistoryStore((s2) => s2.saveState);
|
|
160581
161537
|
useEffect(() => {
|
|
160582
|
-
|
|
161538
|
+
const getActiveCanvas = () => canvas2 || useEditorStore.getState().canvas || useHistoryStore.getState().canvas;
|
|
160583
161539
|
const handleCopy = async () => {
|
|
160584
|
-
const
|
|
161540
|
+
const activeCanvas = getActiveCanvas();
|
|
161541
|
+
if (!activeCanvas) return;
|
|
161542
|
+
const activeObject = activeCanvas.getActiveObject();
|
|
160585
161543
|
if (!activeObject || activeObject.name === "workarea") return;
|
|
160586
|
-
const cloned = await activeObject.clone();
|
|
160587
|
-
|
|
161544
|
+
const cloned = await activeObject.clone(HISTORY_CUSTOM_PROPERTIES);
|
|
161545
|
+
globalClipboard = cloned;
|
|
161546
|
+
clipboardSourcePage = useHistoryStore.getState().currentPage;
|
|
160588
161547
|
};
|
|
160589
161548
|
const handlePaste = async () => {
|
|
160590
|
-
|
|
160591
|
-
|
|
160592
|
-
|
|
161549
|
+
const activeCanvas = getActiveCanvas();
|
|
161550
|
+
if (!activeCanvas || !globalClipboard) return;
|
|
161551
|
+
const currentSide = useHistoryStore.getState().currentPage;
|
|
161552
|
+
const isCrossPagePaste = clipboardSourcePage && clipboardSourcePage !== currentSide;
|
|
161553
|
+
const clonedObj = await globalClipboard.clone(HISTORY_CUSTOM_PROPERTIES);
|
|
161554
|
+
activeCanvas.discardActiveObject();
|
|
161555
|
+
const offset = isCrossPagePaste ? 0 : OFFSET;
|
|
160593
161556
|
clonedObj.set({
|
|
160594
|
-
left: (clonedObj.left ?? 0) +
|
|
160595
|
-
top: (clonedObj.top ?? 0) +
|
|
160596
|
-
evented: true
|
|
161557
|
+
left: (clonedObj.left ?? 0) + offset,
|
|
161558
|
+
top: (clonedObj.top ?? 0) + offset,
|
|
161559
|
+
evented: true,
|
|
161560
|
+
selectable: true
|
|
160597
161561
|
});
|
|
160598
|
-
|
|
160599
|
-
|
|
160600
|
-
|
|
161562
|
+
clonedObj.__pageSide = currentSide;
|
|
161563
|
+
globalClipboard.set({
|
|
161564
|
+
left: (globalClipboard.left ?? 0) + OFFSET,
|
|
161565
|
+
top: (globalClipboard.top ?? 0) + OFFSET
|
|
160601
161566
|
});
|
|
161567
|
+
clipboardSourcePage = currentSide;
|
|
161568
|
+
const ensureTextLoaded = async (obj) => {
|
|
161569
|
+
if (obj.type === "textbox" || obj.type === "text" || obj.type === "i-text") {
|
|
161570
|
+
if (obj.fontFamily) {
|
|
161571
|
+
try {
|
|
161572
|
+
await document.fonts.load(`16px "${obj.fontFamily}"`);
|
|
161573
|
+
} catch {
|
|
161574
|
+
}
|
|
161575
|
+
}
|
|
161576
|
+
obj.initDimensions?.();
|
|
161577
|
+
obj.set("dirty", true);
|
|
161578
|
+
}
|
|
161579
|
+
};
|
|
160602
161580
|
if (clonedObj.type?.toLowerCase() === "activeselection") {
|
|
160603
|
-
|
|
160604
|
-
|
|
160605
|
-
|
|
160606
|
-
|
|
160607
|
-
|
|
161581
|
+
const selection = clonedObj;
|
|
161582
|
+
selection.canvas = activeCanvas;
|
|
161583
|
+
await Promise.all(
|
|
161584
|
+
selection.getObjects().map(async (obj) => {
|
|
161585
|
+
await ensureTextLoaded(obj);
|
|
161586
|
+
obj.__pageSide = currentSide;
|
|
161587
|
+
activeCanvas.add(obj);
|
|
161588
|
+
})
|
|
161589
|
+
);
|
|
161590
|
+
selection.setCoords();
|
|
160608
161591
|
} else {
|
|
160609
|
-
|
|
161592
|
+
await ensureTextLoaded(clonedObj);
|
|
161593
|
+
clonedObj.setCoords();
|
|
161594
|
+
activeCanvas.add(clonedObj);
|
|
160610
161595
|
}
|
|
160611
|
-
|
|
160612
|
-
|
|
161596
|
+
activeCanvas.setActiveObject(clonedObj);
|
|
161597
|
+
bringOverlaysToFront(activeCanvas);
|
|
161598
|
+
activeCanvas.requestRenderAll();
|
|
160613
161599
|
saveState();
|
|
160614
161600
|
};
|
|
160615
161601
|
const handleKeyDown = (e3) => {
|
|
@@ -160617,25 +161603,53 @@ function useKeyboardEvents(canvas2) {
|
|
|
160617
161603
|
const isInput = activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement || activeElement?.isContentEditable;
|
|
160618
161604
|
if (isInput) return;
|
|
160619
161605
|
const isModifier = e3.ctrlKey || e3.metaKey;
|
|
160620
|
-
if (isModifier
|
|
161606
|
+
if (!isModifier) return;
|
|
161607
|
+
const isZ = e3.key?.toLowerCase() === "z" || e3.code === "KeyZ";
|
|
161608
|
+
const isY = e3.key?.toLowerCase() === "y" || e3.code === "KeyY";
|
|
161609
|
+
const isC = e3.key?.toLowerCase() === "c" || e3.code === "KeyC";
|
|
161610
|
+
const isV = e3.key?.toLowerCase() === "v" || e3.code === "KeyV";
|
|
161611
|
+
const isA = e3.key?.toLowerCase() === "a" || e3.code === "KeyA";
|
|
161612
|
+
if (isZ && !e3.shiftKey) {
|
|
161613
|
+
e3.preventDefault();
|
|
161614
|
+
useHistoryStore.getState().undo();
|
|
161615
|
+
return;
|
|
161616
|
+
}
|
|
161617
|
+
if (isZ && e3.shiftKey || isY) {
|
|
161618
|
+
e3.preventDefault();
|
|
161619
|
+
useHistoryStore.getState().redoAction();
|
|
161620
|
+
return;
|
|
161621
|
+
}
|
|
161622
|
+
if (isC) {
|
|
160621
161623
|
e3.preventDefault();
|
|
160622
161624
|
handleCopy();
|
|
161625
|
+
return;
|
|
160623
161626
|
}
|
|
160624
|
-
if (
|
|
161627
|
+
if (isV) {
|
|
160625
161628
|
e3.preventDefault();
|
|
160626
161629
|
handlePaste();
|
|
161630
|
+
return;
|
|
160627
161631
|
}
|
|
160628
|
-
if (
|
|
160629
|
-
|
|
160630
|
-
if (
|
|
160631
|
-
|
|
160632
|
-
|
|
160633
|
-
|
|
161632
|
+
if (isA) {
|
|
161633
|
+
const activeCanvas = getActiveCanvas();
|
|
161634
|
+
if (activeCanvas) {
|
|
161635
|
+
e3.preventDefault();
|
|
161636
|
+
const selectableObjects = activeCanvas.getObjects().filter(
|
|
161637
|
+
(obj) => obj.name !== "workarea" && !obj.name?.startsWith("overlay-") && !obj.name?.startsWith("guideline-") && obj.selectable !== false
|
|
161638
|
+
);
|
|
161639
|
+
if (selectableObjects.length > 0) {
|
|
161640
|
+
activeCanvas.discardActiveObject();
|
|
161641
|
+
const sel = new Qo(selectableObjects, { canvas: activeCanvas });
|
|
161642
|
+
activeCanvas.setActiveObject(sel);
|
|
161643
|
+
activeCanvas.requestRenderAll();
|
|
161644
|
+
}
|
|
160634
161645
|
}
|
|
161646
|
+
return;
|
|
160635
161647
|
}
|
|
160636
|
-
|
|
161648
|
+
const isQuote = e3.key === "'" || e3.code === "Quote";
|
|
161649
|
+
if (isQuote) {
|
|
160637
161650
|
e3.preventDefault();
|
|
160638
|
-
|
|
161651
|
+
useEditorStore.getState().toggleGrid();
|
|
161652
|
+
return;
|
|
160639
161653
|
}
|
|
160640
161654
|
};
|
|
160641
161655
|
window.addEventListener("keydown", handleKeyDown);
|
|
@@ -160651,17 +161665,23 @@ function Workspace() {
|
|
|
160651
161665
|
const workareaRef = useRef(null);
|
|
160652
161666
|
const panHandlersRef = useRef(null);
|
|
160653
161667
|
const isCropping = useEditorStore((s2) => s2.isCropping);
|
|
161668
|
+
const canvas2 = useEditorStore((s2) => s2.canvas);
|
|
160654
161669
|
const setCanvas = useEditorStore((s2) => s2.setCanvas);
|
|
161670
|
+
const showGrid = useEditorStore((s2) => s2.showGrid);
|
|
161671
|
+
const toggleGrid = useEditorStore((s2) => s2.toggleGrid);
|
|
160655
161672
|
const setSelection = useSelectionStore((s2) => s2.setSelection);
|
|
160656
161673
|
const clearSelection = useSelectionStore((s2) => s2.clearSelection);
|
|
161674
|
+
const drawTool = useDrawStore((s2) => s2.drawTool);
|
|
161675
|
+
const setDrawTool = useDrawStore((s2) => s2.setDrawTool);
|
|
160657
161676
|
const productData = useProductStore((s2) => s2.productData);
|
|
160658
|
-
const
|
|
160659
|
-
const [currentPage, setCurrentPage] = useState("front");
|
|
160660
|
-
const [isPanning, setIsPanning] = useState(false);
|
|
161677
|
+
const editorPages = useEditorStore((s2) => s2.pages);
|
|
160661
161678
|
const pageJSON = useRef({
|
|
160662
161679
|
front: null,
|
|
160663
161680
|
back: null
|
|
160664
161681
|
});
|
|
161682
|
+
const isTwoSided = productData?.optionsMap?.printing === "4/4" || !!pageJSON.current?.back || !!editorPages?.back;
|
|
161683
|
+
const [currentPage, setCurrentPage] = useState("front");
|
|
161684
|
+
const [isPanning, setIsPanning] = useState(false);
|
|
160665
161685
|
useEffect(() => {
|
|
160666
161686
|
if (!canvasRef.current || !containerRef.current) return;
|
|
160667
161687
|
if (fabricRef.current) return;
|
|
@@ -160703,20 +161723,20 @@ function Workspace() {
|
|
|
160703
161723
|
}
|
|
160704
161724
|
if (!isMounted || !canvasRef.current || !containerRef.current || fabricRef.current) return;
|
|
160705
161725
|
initializeRotateImage();
|
|
160706
|
-
const
|
|
160707
|
-
fabricRef.current =
|
|
160708
|
-
setCanvas(
|
|
160709
|
-
useHistoryStore.getState().setCanvas(
|
|
160710
|
-
const workarea = createWorkarea(
|
|
161726
|
+
const canvas22 = initializeCanvas(canvasRef.current, containerRef.current);
|
|
161727
|
+
fabricRef.current = canvas22;
|
|
161728
|
+
setCanvas(canvas22);
|
|
161729
|
+
useHistoryStore.getState().setCanvas(canvas22);
|
|
161730
|
+
const workarea = createWorkarea(canvas22, containerRef.current, product);
|
|
160711
161731
|
workareaRef.current = workarea;
|
|
160712
|
-
const cleanupHistory = setupHistoryRecording(
|
|
160713
|
-
const cleanupCrop = setupDoublClickCrop(
|
|
161732
|
+
const cleanupHistory = setupHistoryRecording(canvas22);
|
|
161733
|
+
const cleanupCrop = setupDoublClickCrop(canvas22);
|
|
160714
161734
|
const cleanupSelection = setupSelectionHandlers(
|
|
160715
|
-
|
|
161735
|
+
canvas22,
|
|
160716
161736
|
setSelection,
|
|
160717
161737
|
clearSelection
|
|
160718
161738
|
);
|
|
160719
|
-
const cleanupObjectAdded = setupObjectAddedHandler(
|
|
161739
|
+
const cleanupObjectAdded = setupObjectAddedHandler(canvas22);
|
|
160720
161740
|
if (isTextDesign && templateId) {
|
|
160721
161741
|
const item = await ShapeService.getTextTemplateById(templateId);
|
|
160722
161742
|
if (item?.jsonData) {
|
|
@@ -160724,7 +161744,7 @@ function Workspace() {
|
|
|
160724
161744
|
pageJSON.current.front = parsed;
|
|
160725
161745
|
await loadTemplateFontsIntoHead(null, parsed);
|
|
160726
161746
|
useHistoryStore.setState({ isRestoring: true });
|
|
160727
|
-
await loadPageToCanvas(
|
|
161747
|
+
await loadPageToCanvas(canvas22, parsed);
|
|
160728
161748
|
useHistoryStore.setState({ isRestoring: false });
|
|
160729
161749
|
useHistoryStore.getState().saveState();
|
|
160730
161750
|
}
|
|
@@ -160745,7 +161765,7 @@ function Workspace() {
|
|
|
160745
161765
|
if (frontData) {
|
|
160746
161766
|
pageJSON.current.front = frontData;
|
|
160747
161767
|
useHistoryStore.setState({ isRestoring: true });
|
|
160748
|
-
await loadPageToCanvas(
|
|
161768
|
+
await loadPageToCanvas(canvas22, frontData);
|
|
160749
161769
|
useHistoryStore.setState({ isRestoring: false });
|
|
160750
161770
|
useHistoryStore.getState().saveState();
|
|
160751
161771
|
}
|
|
@@ -160762,10 +161782,10 @@ function Workspace() {
|
|
|
160762
161782
|
cleanupCrop();
|
|
160763
161783
|
cleanupSelection();
|
|
160764
161784
|
cleanupObjectAdded();
|
|
160765
|
-
if (
|
|
160766
|
-
|
|
161785
|
+
if (canvas22.__cleanupResizeObserver) {
|
|
161786
|
+
canvas22.__cleanupResizeObserver();
|
|
160767
161787
|
}
|
|
160768
|
-
|
|
161788
|
+
canvas22.dispose();
|
|
160769
161789
|
fabricRef.current = null;
|
|
160770
161790
|
setCanvas(null);
|
|
160771
161791
|
clearSelection();
|
|
@@ -160803,23 +161823,30 @@ function Workspace() {
|
|
|
160803
161823
|
return setupBorderSync(fabricRef.current);
|
|
160804
161824
|
}, [fabricRef.current]);
|
|
160805
161825
|
useEffect(() => {
|
|
160806
|
-
const
|
|
160807
|
-
if (!
|
|
160808
|
-
removePanHandlers(
|
|
161826
|
+
const canvas22 = fabricRef.current;
|
|
161827
|
+
if (!canvas22) return;
|
|
161828
|
+
removePanHandlers(canvas22, panHandlersRef.current);
|
|
160809
161829
|
panHandlersRef.current = null;
|
|
160810
161830
|
if (isPanning) {
|
|
160811
|
-
panHandlersRef.current = enablePanMode(
|
|
161831
|
+
panHandlersRef.current = enablePanMode(canvas22);
|
|
160812
161832
|
} else {
|
|
160813
|
-
disablePanMode(
|
|
161833
|
+
disablePanMode(canvas22);
|
|
160814
161834
|
}
|
|
160815
161835
|
return () => {
|
|
160816
|
-
removePanHandlers(
|
|
161836
|
+
removePanHandlers(canvas22, panHandlersRef.current);
|
|
160817
161837
|
};
|
|
160818
161838
|
}, [isPanning]);
|
|
160819
161839
|
useFabricDrawing();
|
|
160820
|
-
useKeyboardEvents(fabricRef.current);
|
|
161840
|
+
useKeyboardEvents(canvas2 || fabricRef.current);
|
|
161841
|
+
useEffect(() => {
|
|
161842
|
+
const activeCanvas = canvas2 || fabricRef.current;
|
|
161843
|
+
if (activeCanvas) {
|
|
161844
|
+
renderGrid(activeCanvas);
|
|
161845
|
+
}
|
|
161846
|
+
}, [showGrid, canvas2, currentPage]);
|
|
160821
161847
|
const switchPage = async (page) => {
|
|
160822
161848
|
if (!fabricRef.current) return;
|
|
161849
|
+
setDrawTool("select");
|
|
160823
161850
|
await switchCanvasPage(
|
|
160824
161851
|
fabricRef.current,
|
|
160825
161852
|
currentPage,
|
|
@@ -160830,10 +161857,50 @@ function Workspace() {
|
|
|
160830
161857
|
);
|
|
160831
161858
|
useEditorStore.getState().setPages({ ...pageJSON.current });
|
|
160832
161859
|
setCurrentPage(page);
|
|
161860
|
+
renderGrid(fabricRef.current);
|
|
161861
|
+
};
|
|
161862
|
+
const copyFrontToBack = async () => {
|
|
161863
|
+
if (!fabricRef.current) return;
|
|
161864
|
+
useLoaderStore.getState().startLoading();
|
|
161865
|
+
try {
|
|
161866
|
+
if (currentPage === "front") {
|
|
161867
|
+
pageJSON.current.front = fabricRef.current.toObject(CUSTOM_PROPERTIES);
|
|
161868
|
+
}
|
|
161869
|
+
let frontData = pageJSON.current.front || useEditorStore.getState().pages?.front;
|
|
161870
|
+
if (!frontData) {
|
|
161871
|
+
console.warn("No front design found to copy");
|
|
161872
|
+
return;
|
|
161873
|
+
}
|
|
161874
|
+
const clonedFrontJson = JSON.parse(JSON.stringify(frontData));
|
|
161875
|
+
await loadTemplateFontsIntoHead(null, clonedFrontJson);
|
|
161876
|
+
pageJSON.current.back = clonedFrontJson;
|
|
161877
|
+
useEditorStore.getState().setPages({
|
|
161878
|
+
...useEditorStore.getState().pages,
|
|
161879
|
+
...pageJSON.current,
|
|
161880
|
+
back: clonedFrontJson
|
|
161881
|
+
});
|
|
161882
|
+
clearSelection();
|
|
161883
|
+
useHistoryStore.getState().setPage("back");
|
|
161884
|
+
setCurrentPage("back");
|
|
161885
|
+
await loadPageToCanvas(fabricRef.current, clonedFrontJson, "back");
|
|
161886
|
+
const workarea = fabricRef.current.getObjects().find((obj) => obj.name === "workarea");
|
|
161887
|
+
if (workarea) {
|
|
161888
|
+
workarea.__pageSide = "back";
|
|
161889
|
+
createOverlays(fabricRef.current, workarea, "back");
|
|
161890
|
+
bringOverlaysToFront(fabricRef.current);
|
|
161891
|
+
}
|
|
161892
|
+
fabricRef.current.requestRenderAll();
|
|
161893
|
+
renderGrid(fabricRef.current);
|
|
161894
|
+
useHistoryStore.getState().saveState();
|
|
161895
|
+
} catch (err) {
|
|
161896
|
+
console.error("Failed to copy front design to back:", err);
|
|
161897
|
+
} finally {
|
|
161898
|
+
useLoaderStore.getState().stopLoading();
|
|
161899
|
+
}
|
|
160833
161900
|
};
|
|
160834
161901
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
160835
161902
|
/* @__PURE__ */ jsxs("div", { className: "absolute bottom-6 z-50 flex items-center bg-white/80 backdrop-blur-xl rounded-2xl shadow-[0_8px_32px_rgba(0,0,0,0.12)] px-1.5 py-1.5 border border-white/40 ring-1 ring-black/5", children: [
|
|
160836
|
-
/* @__PURE__ */ jsxs("div", { className: "flex bg-gray-200/50 p-1 rounded-xl", children: [
|
|
161903
|
+
/* @__PURE__ */ jsxs("div", { className: "flex bg-gray-200/50 p-1 rounded-xl items-center gap-1", children: [
|
|
160837
161904
|
/* @__PURE__ */ jsx(
|
|
160838
161905
|
"button",
|
|
160839
161906
|
{
|
|
@@ -160849,6 +161916,37 @@ function Workspace() {
|
|
|
160849
161916
|
className: `px-4 py-2 rounded-lg text-[11px] font-bold tracking-wider transition-all duration-200 cursor-pointer ${currentPage === "back" ? "bg-white text-[#02bc71] shadow-sm" : "text-gray-500 hover:text-gray-700"}`,
|
|
160850
161917
|
children: "BACK"
|
|
160851
161918
|
}
|
|
161919
|
+
),
|
|
161920
|
+
isTwoSided && /* @__PURE__ */ jsxs(
|
|
161921
|
+
"button",
|
|
161922
|
+
{
|
|
161923
|
+
id: "copy-front-to-back-btn",
|
|
161924
|
+
type: "button",
|
|
161925
|
+
onClick: copyFrontToBack,
|
|
161926
|
+
title: "Copy front design to back design",
|
|
161927
|
+
className: "flex items-center gap-1.5 px-3 py-1.5 ml-1 rounded-lg text-[10px] font-bold tracking-wider bg-white/90 hover:bg-white text-gray-700 hover:text-[#02bc71] border border-gray-300/60 shadow-xs transition-all duration-150 cursor-pointer active:scale-95",
|
|
161928
|
+
children: [
|
|
161929
|
+
/* @__PURE__ */ jsxs(
|
|
161930
|
+
"svg",
|
|
161931
|
+
{
|
|
161932
|
+
width: "13",
|
|
161933
|
+
height: "13",
|
|
161934
|
+
viewBox: "0 0 24 24",
|
|
161935
|
+
fill: "none",
|
|
161936
|
+
stroke: "currentColor",
|
|
161937
|
+
strokeWidth: "2.2",
|
|
161938
|
+
strokeLinecap: "round",
|
|
161939
|
+
strokeLinejoin: "round",
|
|
161940
|
+
className: "text-[#02bc71]",
|
|
161941
|
+
children: [
|
|
161942
|
+
/* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
|
|
161943
|
+
/* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
|
|
161944
|
+
]
|
|
161945
|
+
}
|
|
161946
|
+
),
|
|
161947
|
+
/* @__PURE__ */ jsx("span", { children: "COPY FRONT TO BACK" })
|
|
161948
|
+
]
|
|
161949
|
+
}
|
|
160852
161950
|
)
|
|
160853
161951
|
] }),
|
|
160854
161952
|
/* @__PURE__ */ jsx("div", { className: "w-[1px] h-6 bg-gray-300/50 mx-3" }),
|
|
@@ -160856,20 +161954,35 @@ function Workspace() {
|
|
|
160856
161954
|
/* @__PURE__ */ jsx(
|
|
160857
161955
|
"button",
|
|
160858
161956
|
{
|
|
160859
|
-
onClick: () =>
|
|
161957
|
+
onClick: () => {
|
|
161958
|
+
setIsPanning(false);
|
|
161959
|
+
setDrawTool("select");
|
|
161960
|
+
},
|
|
160860
161961
|
title: "Selection Mode",
|
|
160861
|
-
className: `p-2 rounded-xl transition-all duration-200 ${!isPanning ? "bg-[#02bc71] text-white shadow-lg shadow-
|
|
161962
|
+
className: `p-2 rounded-xl transition-all duration-200 cursor-pointer ${!isPanning && drawTool === "select" ? "bg-[#02bc71] text-white shadow-lg shadow-green-200" : "text-gray-400 hover:bg-gray-100 hover:text-gray-600"}`,
|
|
160862
161963
|
children: /* @__PURE__ */ jsx(PointerIcon, {})
|
|
160863
161964
|
}
|
|
160864
161965
|
),
|
|
160865
161966
|
/* @__PURE__ */ jsx(
|
|
160866
161967
|
"button",
|
|
160867
161968
|
{
|
|
160868
|
-
onClick: () =>
|
|
161969
|
+
onClick: () => {
|
|
161970
|
+
setIsPanning(true);
|
|
161971
|
+
setDrawTool("select");
|
|
161972
|
+
},
|
|
160869
161973
|
title: "Hand Mode",
|
|
160870
|
-
className: `p-2 rounded-xl transition-all duration-200 ${isPanning ? "bg-[#02bc71] text-white shadow-lg shadow-
|
|
161974
|
+
className: `p-2 rounded-xl transition-all duration-200 cursor-pointer ${isPanning ? "bg-[#02bc71] text-white shadow-lg shadow-green-200" : "text-gray-400 hover:bg-gray-100 hover:text-gray-600"}`,
|
|
160871
161975
|
children: /* @__PURE__ */ jsx(HandIcon, {})
|
|
160872
161976
|
}
|
|
161977
|
+
),
|
|
161978
|
+
/* @__PURE__ */ jsx(
|
|
161979
|
+
"button",
|
|
161980
|
+
{
|
|
161981
|
+
onClick: () => toggleGrid(),
|
|
161982
|
+
title: showGrid ? "Hide Grid (Ctrl+')" : "Show Grid (Ctrl+')",
|
|
161983
|
+
className: `p-2 rounded-xl transition-all duration-200 cursor-pointer ${showGrid ? "bg-[#02bc71] text-white shadow-lg shadow-green-200" : "text-gray-400 hover:bg-gray-100 hover:text-gray-600"}`,
|
|
161984
|
+
children: /* @__PURE__ */ jsx(GridIcon, {})
|
|
161985
|
+
}
|
|
160873
161986
|
)
|
|
160874
161987
|
] }),
|
|
160875
161988
|
/* @__PURE__ */ jsx("div", { className: "w-[1px] h-6 bg-gray-300/50 mx-3" }),
|