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