vibrant-tool 0.1.10 → 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/types/shape.types.d.ts +1 -0
- package/dist/lib/utils/alignment-guidelines.d.ts +9 -0
- package/dist/lib/utils/page-manager.d.ts +1 -0
- package/dist/modules/components/all-elements/index.d.ts +4 -1
- package/dist/vibrant-tool.css +130 -10
- package/dist/vibrant-tool.es.js +988 -361
- package/dist/vibrant-tool.umd.js +988 -361
- package/package.json +1 -1
package/dist/vibrant-tool.es.js
CHANGED
|
@@ -9017,6 +9017,248 @@ const useProductStore = create((set) => ({
|
|
|
9017
9017
|
}
|
|
9018
9018
|
}
|
|
9019
9019
|
}));
|
|
9020
|
+
const FontService = {
|
|
9021
|
+
async getAll(data) {
|
|
9022
|
+
const res = await apiFetch(`/api/fonts?page=${data.page}&limit=${data.limit}&status=true`, {
|
|
9023
|
+
method: "GET"
|
|
9024
|
+
});
|
|
9025
|
+
return res;
|
|
9026
|
+
}
|
|
9027
|
+
};
|
|
9028
|
+
let cachedFontsPromise = null;
|
|
9029
|
+
async function fetchAllFontsCached() {
|
|
9030
|
+
if (!cachedFontsPromise) {
|
|
9031
|
+
cachedFontsPromise = FontService.getAll({ page: 1, limit: 1e3 }).then((res) => res?.data || []).catch((err) => {
|
|
9032
|
+
console.warn("Failed to fetch all fonts:", err);
|
|
9033
|
+
cachedFontsPromise = null;
|
|
9034
|
+
return [];
|
|
9035
|
+
});
|
|
9036
|
+
}
|
|
9037
|
+
return cachedFontsPromise;
|
|
9038
|
+
}
|
|
9039
|
+
async function injectFontFace(fontName, fontUrl) {
|
|
9040
|
+
if (!fontName || !fontUrl || typeof document === "undefined") return;
|
|
9041
|
+
const cleanedName = fontName.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9042
|
+
const id = `font-${cleanedName.replace(/\s+/g, "-").toLowerCase()}`;
|
|
9043
|
+
if (!document.getElementById(id)) {
|
|
9044
|
+
const style = document.createElement("style");
|
|
9045
|
+
style.id = id;
|
|
9046
|
+
style.innerHTML = `
|
|
9047
|
+
@font-face {
|
|
9048
|
+
font-family: '${cleanedName}';
|
|
9049
|
+
src: url('${fontUrl}');
|
|
9050
|
+
font-display: swap;
|
|
9051
|
+
}
|
|
9052
|
+
`;
|
|
9053
|
+
document.head.appendChild(style);
|
|
9054
|
+
}
|
|
9055
|
+
try {
|
|
9056
|
+
await document.fonts.load(`16px "${cleanedName}"`);
|
|
9057
|
+
await document.fonts.ready;
|
|
9058
|
+
} catch {
|
|
9059
|
+
}
|
|
9060
|
+
}
|
|
9061
|
+
async function loadTemplateFontsIntoHead(templateFonts, pagesDesignData) {
|
|
9062
|
+
if (typeof document === "undefined") return;
|
|
9063
|
+
const loadPromises = [];
|
|
9064
|
+
if (Array.isArray(templateFonts) && templateFonts.length > 0) {
|
|
9065
|
+
templateFonts.forEach((f) => {
|
|
9066
|
+
if (f && typeof f === "object") {
|
|
9067
|
+
const fontName = f.fontName || f.fontFamily;
|
|
9068
|
+
const fontUrl = f.fontFile;
|
|
9069
|
+
if (fontName && fontUrl) {
|
|
9070
|
+
loadPromises.push(injectFontFace(fontName, fontUrl));
|
|
9071
|
+
}
|
|
9072
|
+
if (f.fontFamily && f.fontFamily !== fontName && fontUrl) {
|
|
9073
|
+
loadPromises.push(injectFontFace(f.fontFamily, fontUrl));
|
|
9074
|
+
}
|
|
9075
|
+
}
|
|
9076
|
+
});
|
|
9077
|
+
}
|
|
9078
|
+
const designs = Array.isArray(pagesDesignData) ? pagesDesignData : [pagesDesignData];
|
|
9079
|
+
const usedFontNames = /* @__PURE__ */ new Set();
|
|
9080
|
+
designs.forEach((d2) => {
|
|
9081
|
+
if (d2) {
|
|
9082
|
+
const extracted = extractFontFamilies(d2);
|
|
9083
|
+
extracted.forEach((name) => usedFontNames.add(name));
|
|
9084
|
+
}
|
|
9085
|
+
});
|
|
9086
|
+
if (usedFontNames.size > 0) {
|
|
9087
|
+
try {
|
|
9088
|
+
const allFonts = await fetchAllFontsCached();
|
|
9089
|
+
usedFontNames.forEach((name) => {
|
|
9090
|
+
const lower = name.toLowerCase();
|
|
9091
|
+
const matched = allFonts.find((f) => {
|
|
9092
|
+
if (f.fontName && f.fontName.toLowerCase() === lower) return true;
|
|
9093
|
+
if (f.fontFamily && f.fontFamily.toLowerCase() === lower) return true;
|
|
9094
|
+
const children = f.childFonts || f.chieldFonts || [];
|
|
9095
|
+
return children.some(
|
|
9096
|
+
(c2) => c2.fontName && c2.fontName.toLowerCase() === lower || c2.fontFamily && c2.fontFamily.toLowerCase() === lower
|
|
9097
|
+
);
|
|
9098
|
+
});
|
|
9099
|
+
if (matched) {
|
|
9100
|
+
if (matched.fontName && matched.fontFile) {
|
|
9101
|
+
loadPromises.push(injectFontFace(matched.fontName, matched.fontFile));
|
|
9102
|
+
}
|
|
9103
|
+
if (matched.fontFamily && matched.fontFile) {
|
|
9104
|
+
loadPromises.push(injectFontFace(matched.fontFamily, matched.fontFile));
|
|
9105
|
+
}
|
|
9106
|
+
const children = matched.childFonts || matched.chieldFonts || [];
|
|
9107
|
+
children.forEach((c2) => {
|
|
9108
|
+
if (c2.fontName && c2.fontFile) {
|
|
9109
|
+
loadPromises.push(injectFontFace(c2.fontName, c2.fontFile));
|
|
9110
|
+
}
|
|
9111
|
+
});
|
|
9112
|
+
}
|
|
9113
|
+
});
|
|
9114
|
+
} catch (err) {
|
|
9115
|
+
console.warn("Failed to fetch fonts list for font injection:", err);
|
|
9116
|
+
}
|
|
9117
|
+
}
|
|
9118
|
+
await Promise.allSettled(loadPromises);
|
|
9119
|
+
try {
|
|
9120
|
+
await document.fonts.ready;
|
|
9121
|
+
} catch {
|
|
9122
|
+
}
|
|
9123
|
+
}
|
|
9124
|
+
function extractFontFamilies(designJson) {
|
|
9125
|
+
if (!designJson) return [];
|
|
9126
|
+
const fonts = /* @__PURE__ */ new Set();
|
|
9127
|
+
const traverse = (obj) => {
|
|
9128
|
+
if (!obj) return;
|
|
9129
|
+
if (obj.fontFamily && typeof obj.fontFamily === "string") {
|
|
9130
|
+
const cleaned = obj.fontFamily.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9131
|
+
if (cleaned) fonts.add(cleaned);
|
|
9132
|
+
}
|
|
9133
|
+
if (obj.fontId && typeof obj.fontId === "string") {
|
|
9134
|
+
fonts.add(obj.fontId.trim());
|
|
9135
|
+
}
|
|
9136
|
+
if (obj.styles && typeof obj.styles === "object") {
|
|
9137
|
+
Object.values(obj.styles).forEach((line) => {
|
|
9138
|
+
if (line && typeof line === "object") {
|
|
9139
|
+
Object.values(line).forEach((charStyle) => {
|
|
9140
|
+
if (charStyle?.fontFamily && typeof charStyle.fontFamily === "string") {
|
|
9141
|
+
const cleaned = charStyle.fontFamily.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9142
|
+
if (cleaned) fonts.add(cleaned);
|
|
9143
|
+
}
|
|
9144
|
+
});
|
|
9145
|
+
}
|
|
9146
|
+
});
|
|
9147
|
+
}
|
|
9148
|
+
if (Array.isArray(obj.objects)) {
|
|
9149
|
+
obj.objects.forEach(traverse);
|
|
9150
|
+
}
|
|
9151
|
+
};
|
|
9152
|
+
if (Array.isArray(designJson.objects)) {
|
|
9153
|
+
designJson.objects.forEach(traverse);
|
|
9154
|
+
} else if (Array.isArray(designJson)) {
|
|
9155
|
+
designJson.forEach(traverse);
|
|
9156
|
+
} else {
|
|
9157
|
+
traverse(designJson);
|
|
9158
|
+
}
|
|
9159
|
+
return Array.from(fonts);
|
|
9160
|
+
}
|
|
9161
|
+
async function resolveFontIds(fontNames) {
|
|
9162
|
+
if (!fontNames || !fontNames.length) return [];
|
|
9163
|
+
try {
|
|
9164
|
+
const allFonts = await fetchAllFontsCached();
|
|
9165
|
+
const matchedIds = /* @__PURE__ */ new Set();
|
|
9166
|
+
for (const rawName of fontNames) {
|
|
9167
|
+
const name = rawName.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9168
|
+
const lower = name.toLowerCase();
|
|
9169
|
+
if (/^[0-9a-fA-F]{24}$/.test(name)) {
|
|
9170
|
+
matchedIds.add(name);
|
|
9171
|
+
continue;
|
|
9172
|
+
}
|
|
9173
|
+
const matchedFont = allFonts.find((f) => {
|
|
9174
|
+
if (f._id === name) return true;
|
|
9175
|
+
if (f.fontFamily && f.fontFamily.toLowerCase() === lower) return true;
|
|
9176
|
+
if (f.fontName && f.fontName.toLowerCase() === lower) return true;
|
|
9177
|
+
const children = f.childFonts || f.chieldFonts || [];
|
|
9178
|
+
return children.some(
|
|
9179
|
+
(c2) => c2._id === name || c2.fontName && c2.fontName.toLowerCase() === lower || c2.fontFamily && c2.fontFamily.toLowerCase() === lower
|
|
9180
|
+
);
|
|
9181
|
+
});
|
|
9182
|
+
if (matchedFont?._id) {
|
|
9183
|
+
matchedIds.add(matchedFont._id);
|
|
9184
|
+
}
|
|
9185
|
+
}
|
|
9186
|
+
return Array.from(matchedIds);
|
|
9187
|
+
} catch (err) {
|
|
9188
|
+
console.warn("Failed to resolve font IDs:", err);
|
|
9189
|
+
return [];
|
|
9190
|
+
}
|
|
9191
|
+
}
|
|
9192
|
+
async function resolveFontsForExport(designs, templateFonts) {
|
|
9193
|
+
const result = [];
|
|
9194
|
+
const foundNames = /* @__PURE__ */ new Set();
|
|
9195
|
+
if (Array.isArray(templateFonts) && templateFonts.length > 0) {
|
|
9196
|
+
templateFonts.forEach((f) => {
|
|
9197
|
+
if (f && typeof f === "object") {
|
|
9198
|
+
const fontName = f.fontName || f.fontFamily;
|
|
9199
|
+
const fontFile = f.fontFile;
|
|
9200
|
+
if (fontName && fontFile) {
|
|
9201
|
+
result.push({ fontName, fontFamily: f.fontFamily, fontFile });
|
|
9202
|
+
foundNames.add(fontName.toLowerCase());
|
|
9203
|
+
if (f.fontFamily) foundNames.add(f.fontFamily.toLowerCase());
|
|
9204
|
+
}
|
|
9205
|
+
}
|
|
9206
|
+
});
|
|
9207
|
+
}
|
|
9208
|
+
const usedFontNames = /* @__PURE__ */ new Set();
|
|
9209
|
+
const designsArr = Array.isArray(designs) ? designs : [designs];
|
|
9210
|
+
designsArr.forEach((d2) => {
|
|
9211
|
+
if (d2) {
|
|
9212
|
+
const extracted = extractFontFamilies(d2);
|
|
9213
|
+
extracted.forEach((name) => {
|
|
9214
|
+
if (!foundNames.has(name.toLowerCase())) {
|
|
9215
|
+
usedFontNames.add(name);
|
|
9216
|
+
}
|
|
9217
|
+
});
|
|
9218
|
+
}
|
|
9219
|
+
});
|
|
9220
|
+
if (usedFontNames.size > 0) {
|
|
9221
|
+
try {
|
|
9222
|
+
const allFonts = await fetchAllFontsCached();
|
|
9223
|
+
usedFontNames.forEach((name) => {
|
|
9224
|
+
const lower = name.toLowerCase();
|
|
9225
|
+
const matched = allFonts.find((f) => {
|
|
9226
|
+
if (f.fontName && f.fontName.toLowerCase() === lower) return true;
|
|
9227
|
+
if (f.fontFamily && f.fontFamily.toLowerCase() === lower) return true;
|
|
9228
|
+
const children = f.childFonts || f.chieldFonts || [];
|
|
9229
|
+
return children.some(
|
|
9230
|
+
(c2) => c2.fontName && c2.fontName.toLowerCase() === lower || c2.fontFamily && c2.fontFamily.toLowerCase() === lower
|
|
9231
|
+
);
|
|
9232
|
+
});
|
|
9233
|
+
if (matched) {
|
|
9234
|
+
if (matched.fontName && matched.fontFile && !foundNames.has(matched.fontName.toLowerCase())) {
|
|
9235
|
+
result.push({
|
|
9236
|
+
fontName: matched.fontName,
|
|
9237
|
+
fontFamily: matched.fontFamily,
|
|
9238
|
+
fontFile: matched.fontFile
|
|
9239
|
+
});
|
|
9240
|
+
foundNames.add(matched.fontName.toLowerCase());
|
|
9241
|
+
}
|
|
9242
|
+
const children = matched.childFonts || matched.chieldFonts || [];
|
|
9243
|
+
children.forEach((c2) => {
|
|
9244
|
+
const childName = c2.fontName || name;
|
|
9245
|
+
if (c2.fontFile && !foundNames.has(childName.toLowerCase())) {
|
|
9246
|
+
result.push({
|
|
9247
|
+
fontName: childName,
|
|
9248
|
+
fontFamily: c2.fontFamily,
|
|
9249
|
+
fontFile: c2.fontFile
|
|
9250
|
+
});
|
|
9251
|
+
foundNames.add(childName.toLowerCase());
|
|
9252
|
+
}
|
|
9253
|
+
});
|
|
9254
|
+
}
|
|
9255
|
+
});
|
|
9256
|
+
} catch (err) {
|
|
9257
|
+
console.warn("Failed to resolve fonts for export:", err);
|
|
9258
|
+
}
|
|
9259
|
+
}
|
|
9260
|
+
return result;
|
|
9261
|
+
}
|
|
9020
9262
|
async function loadPageToCanvas(canvas2, json, pageSide) {
|
|
9021
9263
|
if (!canvas2) return;
|
|
9022
9264
|
canvas2.discardActiveObject();
|
|
@@ -9024,6 +9266,7 @@ async function loadPageToCanvas(canvas2, json, pageSide) {
|
|
|
9024
9266
|
if (json) {
|
|
9025
9267
|
try {
|
|
9026
9268
|
const parsedJson = typeof json === "string" ? JSON.parse(json) : json;
|
|
9269
|
+
await loadTemplateFontsIntoHead(null, parsedJson);
|
|
9027
9270
|
const loadPromise = canvas2.loadFromJSON(parsedJson);
|
|
9028
9271
|
const timeoutPromise = new Promise(
|
|
9029
9272
|
(_2, reject) => setTimeout(() => reject(new Error("Canvas loadFromJSON timeout")), 4e3)
|
|
@@ -9365,7 +9608,23 @@ const HISTORY_CUSTOM_PROPERTIES = [
|
|
|
9365
9608
|
"__borderRect",
|
|
9366
9609
|
"__rotationControlSize",
|
|
9367
9610
|
"__showDegree",
|
|
9368
|
-
"textAlign"
|
|
9611
|
+
"textAlign",
|
|
9612
|
+
"fontFamily",
|
|
9613
|
+
"fontSize",
|
|
9614
|
+
"fontWeight",
|
|
9615
|
+
"fontStyle",
|
|
9616
|
+
"underline",
|
|
9617
|
+
"linethrough",
|
|
9618
|
+
"overline",
|
|
9619
|
+
"fill",
|
|
9620
|
+
"stroke",
|
|
9621
|
+
"strokeWidth",
|
|
9622
|
+
"lineHeight",
|
|
9623
|
+
"charSpacing",
|
|
9624
|
+
"styles",
|
|
9625
|
+
"paintFirst",
|
|
9626
|
+
"strokeUniform",
|
|
9627
|
+
"objectCaching"
|
|
9369
9628
|
];
|
|
9370
9629
|
const useHistoryStore = create((set, get) => ({
|
|
9371
9630
|
canvas: null,
|
|
@@ -9483,248 +9742,6 @@ const useHistoryStore = create((set, get) => ({
|
|
|
9483
9742
|
}
|
|
9484
9743
|
}
|
|
9485
9744
|
}));
|
|
9486
|
-
const FontService = {
|
|
9487
|
-
async getAll(data) {
|
|
9488
|
-
const res = await apiFetch(`/api/fonts?page=${data.page}&limit=${data.limit}&status=true`, {
|
|
9489
|
-
method: "GET"
|
|
9490
|
-
});
|
|
9491
|
-
return res;
|
|
9492
|
-
}
|
|
9493
|
-
};
|
|
9494
|
-
let cachedFontsPromise = null;
|
|
9495
|
-
async function fetchAllFontsCached() {
|
|
9496
|
-
if (!cachedFontsPromise) {
|
|
9497
|
-
cachedFontsPromise = FontService.getAll({ page: 1, limit: 1e3 }).then((res) => res?.data || []).catch((err) => {
|
|
9498
|
-
console.warn("Failed to fetch all fonts:", err);
|
|
9499
|
-
cachedFontsPromise = null;
|
|
9500
|
-
return [];
|
|
9501
|
-
});
|
|
9502
|
-
}
|
|
9503
|
-
return cachedFontsPromise;
|
|
9504
|
-
}
|
|
9505
|
-
async function injectFontFace(fontName, fontUrl) {
|
|
9506
|
-
if (!fontName || !fontUrl || typeof document === "undefined") return;
|
|
9507
|
-
const cleanedName = fontName.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9508
|
-
const id = `font-${cleanedName.replace(/\s+/g, "-").toLowerCase()}`;
|
|
9509
|
-
if (!document.getElementById(id)) {
|
|
9510
|
-
const style = document.createElement("style");
|
|
9511
|
-
style.id = id;
|
|
9512
|
-
style.innerHTML = `
|
|
9513
|
-
@font-face {
|
|
9514
|
-
font-family: '${cleanedName}';
|
|
9515
|
-
src: url('${fontUrl}');
|
|
9516
|
-
font-display: swap;
|
|
9517
|
-
}
|
|
9518
|
-
`;
|
|
9519
|
-
document.head.appendChild(style);
|
|
9520
|
-
}
|
|
9521
|
-
try {
|
|
9522
|
-
await document.fonts.load(`16px "${cleanedName}"`);
|
|
9523
|
-
await document.fonts.ready;
|
|
9524
|
-
} catch {
|
|
9525
|
-
}
|
|
9526
|
-
}
|
|
9527
|
-
async function loadTemplateFontsIntoHead(templateFonts, pagesDesignData) {
|
|
9528
|
-
if (typeof document === "undefined") return;
|
|
9529
|
-
const loadPromises = [];
|
|
9530
|
-
if (Array.isArray(templateFonts) && templateFonts.length > 0) {
|
|
9531
|
-
templateFonts.forEach((f) => {
|
|
9532
|
-
if (f && typeof f === "object") {
|
|
9533
|
-
const fontName = f.fontName || f.fontFamily;
|
|
9534
|
-
const fontUrl = f.fontFile;
|
|
9535
|
-
if (fontName && fontUrl) {
|
|
9536
|
-
loadPromises.push(injectFontFace(fontName, fontUrl));
|
|
9537
|
-
}
|
|
9538
|
-
if (f.fontFamily && f.fontFamily !== fontName && fontUrl) {
|
|
9539
|
-
loadPromises.push(injectFontFace(f.fontFamily, fontUrl));
|
|
9540
|
-
}
|
|
9541
|
-
}
|
|
9542
|
-
});
|
|
9543
|
-
}
|
|
9544
|
-
const designs = Array.isArray(pagesDesignData) ? pagesDesignData : [pagesDesignData];
|
|
9545
|
-
const usedFontNames = /* @__PURE__ */ new Set();
|
|
9546
|
-
designs.forEach((d2) => {
|
|
9547
|
-
if (d2) {
|
|
9548
|
-
const extracted = extractFontFamilies(d2);
|
|
9549
|
-
extracted.forEach((name) => usedFontNames.add(name));
|
|
9550
|
-
}
|
|
9551
|
-
});
|
|
9552
|
-
if (usedFontNames.size > 0) {
|
|
9553
|
-
try {
|
|
9554
|
-
const allFonts = await fetchAllFontsCached();
|
|
9555
|
-
usedFontNames.forEach((name) => {
|
|
9556
|
-
const lower = name.toLowerCase();
|
|
9557
|
-
const matched = allFonts.find((f) => {
|
|
9558
|
-
if (f.fontName && f.fontName.toLowerCase() === lower) return true;
|
|
9559
|
-
if (f.fontFamily && f.fontFamily.toLowerCase() === lower) return true;
|
|
9560
|
-
const children = f.childFonts || f.chieldFonts || [];
|
|
9561
|
-
return children.some(
|
|
9562
|
-
(c2) => c2.fontName && c2.fontName.toLowerCase() === lower || c2.fontFamily && c2.fontFamily.toLowerCase() === lower
|
|
9563
|
-
);
|
|
9564
|
-
});
|
|
9565
|
-
if (matched) {
|
|
9566
|
-
if (matched.fontName && matched.fontFile) {
|
|
9567
|
-
loadPromises.push(injectFontFace(matched.fontName, matched.fontFile));
|
|
9568
|
-
}
|
|
9569
|
-
if (matched.fontFamily && matched.fontFile) {
|
|
9570
|
-
loadPromises.push(injectFontFace(matched.fontFamily, matched.fontFile));
|
|
9571
|
-
}
|
|
9572
|
-
const children = matched.childFonts || matched.chieldFonts || [];
|
|
9573
|
-
children.forEach((c2) => {
|
|
9574
|
-
if (c2.fontName && c2.fontFile) {
|
|
9575
|
-
loadPromises.push(injectFontFace(c2.fontName, c2.fontFile));
|
|
9576
|
-
}
|
|
9577
|
-
});
|
|
9578
|
-
}
|
|
9579
|
-
});
|
|
9580
|
-
} catch (err) {
|
|
9581
|
-
console.warn("Failed to fetch fonts list for font injection:", err);
|
|
9582
|
-
}
|
|
9583
|
-
}
|
|
9584
|
-
await Promise.allSettled(loadPromises);
|
|
9585
|
-
try {
|
|
9586
|
-
await document.fonts.ready;
|
|
9587
|
-
} catch {
|
|
9588
|
-
}
|
|
9589
|
-
}
|
|
9590
|
-
function extractFontFamilies(designJson) {
|
|
9591
|
-
if (!designJson) return [];
|
|
9592
|
-
const fonts = /* @__PURE__ */ new Set();
|
|
9593
|
-
const traverse = (obj) => {
|
|
9594
|
-
if (!obj) return;
|
|
9595
|
-
if (obj.fontFamily && typeof obj.fontFamily === "string") {
|
|
9596
|
-
const cleaned = obj.fontFamily.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9597
|
-
if (cleaned) fonts.add(cleaned);
|
|
9598
|
-
}
|
|
9599
|
-
if (obj.fontId && typeof obj.fontId === "string") {
|
|
9600
|
-
fonts.add(obj.fontId.trim());
|
|
9601
|
-
}
|
|
9602
|
-
if (obj.styles && typeof obj.styles === "object") {
|
|
9603
|
-
Object.values(obj.styles).forEach((line) => {
|
|
9604
|
-
if (line && typeof line === "object") {
|
|
9605
|
-
Object.values(line).forEach((charStyle) => {
|
|
9606
|
-
if (charStyle?.fontFamily && typeof charStyle.fontFamily === "string") {
|
|
9607
|
-
const cleaned = charStyle.fontFamily.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9608
|
-
if (cleaned) fonts.add(cleaned);
|
|
9609
|
-
}
|
|
9610
|
-
});
|
|
9611
|
-
}
|
|
9612
|
-
});
|
|
9613
|
-
}
|
|
9614
|
-
if (Array.isArray(obj.objects)) {
|
|
9615
|
-
obj.objects.forEach(traverse);
|
|
9616
|
-
}
|
|
9617
|
-
};
|
|
9618
|
-
if (Array.isArray(designJson.objects)) {
|
|
9619
|
-
designJson.objects.forEach(traverse);
|
|
9620
|
-
} else if (Array.isArray(designJson)) {
|
|
9621
|
-
designJson.forEach(traverse);
|
|
9622
|
-
} else {
|
|
9623
|
-
traverse(designJson);
|
|
9624
|
-
}
|
|
9625
|
-
return Array.from(fonts);
|
|
9626
|
-
}
|
|
9627
|
-
async function resolveFontIds(fontNames) {
|
|
9628
|
-
if (!fontNames || !fontNames.length) return [];
|
|
9629
|
-
try {
|
|
9630
|
-
const allFonts = await fetchAllFontsCached();
|
|
9631
|
-
const matchedIds = /* @__PURE__ */ new Set();
|
|
9632
|
-
for (const rawName of fontNames) {
|
|
9633
|
-
const name = rawName.trim().replace(/^['"]+|['"]+$/g, "");
|
|
9634
|
-
const lower = name.toLowerCase();
|
|
9635
|
-
if (/^[0-9a-fA-F]{24}$/.test(name)) {
|
|
9636
|
-
matchedIds.add(name);
|
|
9637
|
-
continue;
|
|
9638
|
-
}
|
|
9639
|
-
const matchedFont = allFonts.find((f) => {
|
|
9640
|
-
if (f._id === name) return true;
|
|
9641
|
-
if (f.fontFamily && f.fontFamily.toLowerCase() === lower) return true;
|
|
9642
|
-
if (f.fontName && f.fontName.toLowerCase() === lower) return true;
|
|
9643
|
-
const children = f.childFonts || f.chieldFonts || [];
|
|
9644
|
-
return children.some(
|
|
9645
|
-
(c2) => c2._id === name || c2.fontName && c2.fontName.toLowerCase() === lower || c2.fontFamily && c2.fontFamily.toLowerCase() === lower
|
|
9646
|
-
);
|
|
9647
|
-
});
|
|
9648
|
-
if (matchedFont?._id) {
|
|
9649
|
-
matchedIds.add(matchedFont._id);
|
|
9650
|
-
}
|
|
9651
|
-
}
|
|
9652
|
-
return Array.from(matchedIds);
|
|
9653
|
-
} catch (err) {
|
|
9654
|
-
console.warn("Failed to resolve font IDs:", err);
|
|
9655
|
-
return [];
|
|
9656
|
-
}
|
|
9657
|
-
}
|
|
9658
|
-
async function resolveFontsForExport(designs, templateFonts) {
|
|
9659
|
-
const result = [];
|
|
9660
|
-
const foundNames = /* @__PURE__ */ new Set();
|
|
9661
|
-
if (Array.isArray(templateFonts) && templateFonts.length > 0) {
|
|
9662
|
-
templateFonts.forEach((f) => {
|
|
9663
|
-
if (f && typeof f === "object") {
|
|
9664
|
-
const fontName = f.fontName || f.fontFamily;
|
|
9665
|
-
const fontFile = f.fontFile;
|
|
9666
|
-
if (fontName && fontFile) {
|
|
9667
|
-
result.push({ fontName, fontFamily: f.fontFamily, fontFile });
|
|
9668
|
-
foundNames.add(fontName.toLowerCase());
|
|
9669
|
-
if (f.fontFamily) foundNames.add(f.fontFamily.toLowerCase());
|
|
9670
|
-
}
|
|
9671
|
-
}
|
|
9672
|
-
});
|
|
9673
|
-
}
|
|
9674
|
-
const usedFontNames = /* @__PURE__ */ new Set();
|
|
9675
|
-
const designsArr = Array.isArray(designs) ? designs : [designs];
|
|
9676
|
-
designsArr.forEach((d2) => {
|
|
9677
|
-
if (d2) {
|
|
9678
|
-
const extracted = extractFontFamilies(d2);
|
|
9679
|
-
extracted.forEach((name) => {
|
|
9680
|
-
if (!foundNames.has(name.toLowerCase())) {
|
|
9681
|
-
usedFontNames.add(name);
|
|
9682
|
-
}
|
|
9683
|
-
});
|
|
9684
|
-
}
|
|
9685
|
-
});
|
|
9686
|
-
if (usedFontNames.size > 0) {
|
|
9687
|
-
try {
|
|
9688
|
-
const allFonts = await fetchAllFontsCached();
|
|
9689
|
-
usedFontNames.forEach((name) => {
|
|
9690
|
-
const lower = name.toLowerCase();
|
|
9691
|
-
const matched = allFonts.find((f) => {
|
|
9692
|
-
if (f.fontName && f.fontName.toLowerCase() === lower) return true;
|
|
9693
|
-
if (f.fontFamily && f.fontFamily.toLowerCase() === lower) return true;
|
|
9694
|
-
const children = f.childFonts || f.chieldFonts || [];
|
|
9695
|
-
return children.some(
|
|
9696
|
-
(c2) => c2.fontName && c2.fontName.toLowerCase() === lower || c2.fontFamily && c2.fontFamily.toLowerCase() === lower
|
|
9697
|
-
);
|
|
9698
|
-
});
|
|
9699
|
-
if (matched) {
|
|
9700
|
-
if (matched.fontName && matched.fontFile && !foundNames.has(matched.fontName.toLowerCase())) {
|
|
9701
|
-
result.push({
|
|
9702
|
-
fontName: matched.fontName,
|
|
9703
|
-
fontFamily: matched.fontFamily,
|
|
9704
|
-
fontFile: matched.fontFile
|
|
9705
|
-
});
|
|
9706
|
-
foundNames.add(matched.fontName.toLowerCase());
|
|
9707
|
-
}
|
|
9708
|
-
const children = matched.childFonts || matched.chieldFonts || [];
|
|
9709
|
-
children.forEach((c2) => {
|
|
9710
|
-
const childName = c2.fontName || name;
|
|
9711
|
-
if (c2.fontFile && !foundNames.has(childName.toLowerCase())) {
|
|
9712
|
-
result.push({
|
|
9713
|
-
fontName: childName,
|
|
9714
|
-
fontFamily: c2.fontFamily,
|
|
9715
|
-
fontFile: c2.fontFile
|
|
9716
|
-
});
|
|
9717
|
-
foundNames.add(childName.toLowerCase());
|
|
9718
|
-
}
|
|
9719
|
-
});
|
|
9720
|
-
}
|
|
9721
|
-
});
|
|
9722
|
-
} catch (err) {
|
|
9723
|
-
console.warn("Failed to resolve fonts for export:", err);
|
|
9724
|
-
}
|
|
9725
|
-
}
|
|
9726
|
-
return result;
|
|
9727
|
-
}
|
|
9728
9745
|
const WATERMARK_TEXT = "vibrantprintshop";
|
|
9729
9746
|
function createWatermark(curWorkarea, pageSide) {
|
|
9730
9747
|
const curW = curWorkarea.getScaledWidth ? curWorkarea.getScaledWidth() : curWorkarea.width * (curWorkarea.scaleX || 1);
|
|
@@ -9817,8 +9834,18 @@ function exportDesignJSON(canvas2, workarea) {
|
|
|
9817
9834
|
}
|
|
9818
9835
|
const ShapeService = {
|
|
9819
9836
|
async getAll(data) {
|
|
9837
|
+
const params = new URLSearchParams();
|
|
9838
|
+
if (data.page) params.append("page", String(data.page));
|
|
9839
|
+
if (data.limit) params.append("limit", String(data.limit));
|
|
9840
|
+
if (data.category && data.category !== "ALL" && data.category !== "null" && data.category !== "undefined") {
|
|
9841
|
+
params.append("category", data.category);
|
|
9842
|
+
}
|
|
9843
|
+
if (data.search && data.search.trim()) {
|
|
9844
|
+
params.append("search", data.search.trim());
|
|
9845
|
+
}
|
|
9846
|
+
params.append("status", "true");
|
|
9820
9847
|
const res = await apiFetch(
|
|
9821
|
-
`/api/cliparts
|
|
9848
|
+
`/api/cliparts?${params.toString()}`,
|
|
9822
9849
|
{
|
|
9823
9850
|
method: "GET"
|
|
9824
9851
|
}
|
|
@@ -9828,8 +9855,17 @@ const ShapeService = {
|
|
|
9828
9855
|
};
|
|
9829
9856
|
},
|
|
9830
9857
|
async getAllExternal(data) {
|
|
9858
|
+
const params = new URLSearchParams();
|
|
9859
|
+
if (data.page) params.append("page", String(data.page));
|
|
9860
|
+
if (data.limit) params.append("limit", String(data.limit));
|
|
9861
|
+
if (data.keyType) params.append("keyType", data.keyType);
|
|
9862
|
+
if (data.isVector) params.append("isVector", "true");
|
|
9863
|
+
if (data.query && data.query.trim()) {
|
|
9864
|
+
params.append("query", data.query.trim());
|
|
9865
|
+
}
|
|
9866
|
+
params.append("status", "true");
|
|
9831
9867
|
const res = await apiFetch(
|
|
9832
|
-
`/api/external/images
|
|
9868
|
+
`/api/external/images?${params.toString()}`,
|
|
9833
9869
|
{
|
|
9834
9870
|
method: "GET"
|
|
9835
9871
|
}
|
|
@@ -101910,8 +101946,43 @@ function TemplateMockupModal({
|
|
|
101910
101946
|
if (!uploadedImageUrl) {
|
|
101911
101947
|
throw new Error("Server upload returned empty image URL");
|
|
101912
101948
|
}
|
|
101949
|
+
const canvas22 = useEditorStore.getState().canvas;
|
|
101950
|
+
if (!templateId || !canvas22) return;
|
|
101951
|
+
const { pages: historyPages, currentPage } = useHistoryStore.getState();
|
|
101952
|
+
const editorPages = useEditorStore.getState().pages || {};
|
|
101953
|
+
const currentJson = canvas22.toObject(["name"]);
|
|
101954
|
+
const currentImage = getWorkareaDataUrl(canvas22, 1, currentPage);
|
|
101955
|
+
const pages = await Promise.all(
|
|
101956
|
+
["front", "back"].map(async (side, i) => {
|
|
101957
|
+
const isCurrent = currentPage === side;
|
|
101958
|
+
const designData = isCurrent ? currentJson : editorPages[side] || historyPages[side]?.history?.at(-1) || null;
|
|
101959
|
+
if (!designData) return null;
|
|
101960
|
+
const sideImage = isCurrent ? currentImage : await renderPageDataUrl(designData, canvas22, 1, side);
|
|
101961
|
+
const uploadedImageUrl2 = await UploadService.uploadDataUrl(
|
|
101962
|
+
sideImage,
|
|
101963
|
+
`template-${templateId}-${side}-${Date.now()}.png`
|
|
101964
|
+
);
|
|
101965
|
+
return {
|
|
101966
|
+
side,
|
|
101967
|
+
designData,
|
|
101968
|
+
image: uploadedImageUrl2,
|
|
101969
|
+
order: i + 1
|
|
101970
|
+
};
|
|
101971
|
+
})
|
|
101972
|
+
);
|
|
101973
|
+
const validPages = pages.filter(Boolean);
|
|
101974
|
+
const usedFontNames = /* @__PURE__ */ new Set();
|
|
101975
|
+
validPages.forEach((p2) => {
|
|
101976
|
+
if (p2?.designData) {
|
|
101977
|
+
const fontsInPage = extractFontFamilies(p2.designData);
|
|
101978
|
+
fontsInPage.forEach((f) => usedFontNames.add(f));
|
|
101979
|
+
}
|
|
101980
|
+
});
|
|
101981
|
+
const resolvedFontIds = await resolveFontIds(Array.from(usedFontNames));
|
|
101913
101982
|
const res = await TemplateDesignService.updateTemplateDesign(templateId, {
|
|
101914
|
-
image: uploadedImageUrl
|
|
101983
|
+
image: uploadedImageUrl,
|
|
101984
|
+
pages: validPages,
|
|
101985
|
+
fonts: resolvedFontIds
|
|
101915
101986
|
});
|
|
101916
101987
|
if (res && res.success) {
|
|
101917
101988
|
setGenerateSuccess(true);
|
|
@@ -102116,6 +102187,16 @@ function Topbar() {
|
|
|
102116
102187
|
const resetTour = useTourStore((s2) => s2.reset);
|
|
102117
102188
|
const templateId = getQueryParam("templateId") || getQueryParam("id");
|
|
102118
102189
|
const designType = (getQueryParam("designType") || getQueryParam("designtype") || getQueryParam("type"))?.toLowerCase();
|
|
102190
|
+
const handleLeavePage = () => {
|
|
102191
|
+
if (designType === "text") {
|
|
102192
|
+
spaNavigate("/design-configuration/text-templates");
|
|
102193
|
+
return;
|
|
102194
|
+
}
|
|
102195
|
+
if (designType === "template") {
|
|
102196
|
+
spaNavigate("/design-configuration/template-designs");
|
|
102197
|
+
return;
|
|
102198
|
+
}
|
|
102199
|
+
};
|
|
102119
102200
|
const handleSaveTextTemplate = async () => {
|
|
102120
102201
|
const canvas2 = useEditorStore.getState().canvas;
|
|
102121
102202
|
if (!templateId || !canvas2) return;
|
|
@@ -102210,8 +102291,42 @@ function Topbar() {
|
|
|
102210
102291
|
}
|
|
102211
102292
|
};
|
|
102212
102293
|
return /* @__PURE__ */ jsxs("div", { className: "w-full h-14 bg-[#111] text-white flex items-center justify-between px-4 border-b border-gray-800 relative z-[9999] whitespace-nowrap", children: [
|
|
102213
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-
|
|
102214
|
-
/* @__PURE__ */
|
|
102294
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 shrink-0", children: [
|
|
102295
|
+
/* @__PURE__ */ jsxs(
|
|
102296
|
+
"button",
|
|
102297
|
+
{
|
|
102298
|
+
id: "topbar-back-button",
|
|
102299
|
+
type: "button",
|
|
102300
|
+
onClick: handleLeavePage,
|
|
102301
|
+
disabled: isSaving,
|
|
102302
|
+
className: `flex items-center gap-1.5 px-2.5 py-1.5 bg-[#222] hover:bg-[#2c2c2c] active:bg-[#1a1a1a] text-gray-300 hover:text-white border border-gray-700/70 hover:border-gray-500 rounded-lg text-xs font-medium transition-all group shadow-sm ${isSaving ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}`,
|
|
102303
|
+
title: "Back / Leave page",
|
|
102304
|
+
"aria-label": "Back / Leave page",
|
|
102305
|
+
children: [
|
|
102306
|
+
/* @__PURE__ */ jsx(
|
|
102307
|
+
"svg",
|
|
102308
|
+
{
|
|
102309
|
+
className: "w-4 h-4 transition-transform duration-150 group-hover:-translate-x-0.5 text-gray-300 group-hover:text-white",
|
|
102310
|
+
fill: "none",
|
|
102311
|
+
viewBox: "0 0 24 24",
|
|
102312
|
+
stroke: "currentColor",
|
|
102313
|
+
strokeWidth: 2.2,
|
|
102314
|
+
children: /* @__PURE__ */ jsx(
|
|
102315
|
+
"path",
|
|
102316
|
+
{
|
|
102317
|
+
strokeLinecap: "round",
|
|
102318
|
+
strokeLinejoin: "round",
|
|
102319
|
+
d: "M10 19l-7-7m0 0l7-7m-7 7h18"
|
|
102320
|
+
}
|
|
102321
|
+
)
|
|
102322
|
+
}
|
|
102323
|
+
),
|
|
102324
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold tracking-wide", children: "Back" })
|
|
102325
|
+
]
|
|
102326
|
+
}
|
|
102327
|
+
),
|
|
102328
|
+
/* @__PURE__ */ jsx("div", { className: "w-px h-5 bg-gray-700/80 hidden sm:block" }),
|
|
102329
|
+
/* @__PURE__ */ jsx("h1", { className: "text-lg font-semibold tracking-tight", children: "Vibrant" }),
|
|
102215
102330
|
/* @__PURE__ */ jsx("div", { className: "w-px h-6 bg-gray-600 hidden md:block" })
|
|
102216
102331
|
] }),
|
|
102217
102332
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 shrink-0 ml-4", children: [
|
|
@@ -102585,6 +102700,7 @@ function TemplatesPanel() {
|
|
|
102585
102700
|
const [page, setPage] = useState(1);
|
|
102586
102701
|
const scrollRef = useRef(null);
|
|
102587
102702
|
const [hasMore, setHasMore] = useState(true);
|
|
102703
|
+
const templateId = getQueryParam("templateId");
|
|
102588
102704
|
useEffect(() => {
|
|
102589
102705
|
if (!productData?._id) return;
|
|
102590
102706
|
startLoading();
|
|
@@ -102676,7 +102792,7 @@ function TemplatesPanel() {
|
|
|
102676
102792
|
)
|
|
102677
102793
|
] }),
|
|
102678
102794
|
/* @__PURE__ */ jsx("div", { ref: scrollRef, className: "h-[calc(100vh-125px)] overflow-auto custom-scrollbar", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3 pb-12 mx-4", children: [
|
|
102679
|
-
templates.map((tpl) => /* @__PURE__ */ jsx(
|
|
102795
|
+
templates.filter((t2) => t2.image && t2._id !== templateId).map((tpl) => /* @__PURE__ */ jsx(
|
|
102680
102796
|
"button",
|
|
102681
102797
|
{
|
|
102682
102798
|
className: "rounded overflow-hidden group relative cursor-pointer border border-transparent hover:border-[#02bc71] transition-all",
|
|
@@ -103139,26 +103255,63 @@ function AllElements({
|
|
|
103139
103255
|
elements: elements2,
|
|
103140
103256
|
onBack,
|
|
103141
103257
|
scrollRef,
|
|
103142
|
-
onSearch
|
|
103258
|
+
onSearch,
|
|
103259
|
+
initialSearch = "",
|
|
103260
|
+
title,
|
|
103261
|
+
backText
|
|
103143
103262
|
}) {
|
|
103144
|
-
const [search, setSearch] = useState(
|
|
103145
|
-
const { isLoading } = useLoaderStore
|
|
103263
|
+
const [search, setSearch] = useState(initialSearch);
|
|
103264
|
+
const { isLoading } = useLoaderStore();
|
|
103146
103265
|
const { addRecentItem } = useRecentElementsStore();
|
|
103266
|
+
useEffect(() => {
|
|
103267
|
+
setSearch(initialSearch);
|
|
103268
|
+
}, [initialSearch]);
|
|
103147
103269
|
const clearSearch = () => {
|
|
103148
103270
|
setSearch("");
|
|
103149
103271
|
onSearch("");
|
|
103150
103272
|
};
|
|
103151
|
-
const
|
|
103152
|
-
(
|
|
103153
|
-
|
|
103154
|
-
|
|
103155
|
-
|
|
103156
|
-
|
|
103157
|
-
|
|
103158
|
-
|
|
103159
|
-
|
|
103160
|
-
|
|
103161
|
-
|
|
103273
|
+
const handleSearchSubmit = () => {
|
|
103274
|
+
onSearch(search.trim());
|
|
103275
|
+
};
|
|
103276
|
+
const handleKeyDown = (e3) => {
|
|
103277
|
+
e3.stopPropagation();
|
|
103278
|
+
if (e3.key === "Enter") {
|
|
103279
|
+
handleSearchSubmit();
|
|
103280
|
+
}
|
|
103281
|
+
};
|
|
103282
|
+
const dynamicBackLabel = backText || (title ? title : "Back");
|
|
103283
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
|
|
103284
|
+
/* @__PURE__ */ jsx("div", { className: "mx-4 mb-2.5 flex items-center", children: /* @__PURE__ */ jsxs(
|
|
103285
|
+
"button",
|
|
103286
|
+
{
|
|
103287
|
+
type: "button",
|
|
103288
|
+
onClick: onBack,
|
|
103289
|
+
className: "cursor-pointer flex items-center gap-1.5 text-xs font-semibold text-neutral-300 hover:text-white transition-colors group",
|
|
103290
|
+
title: `Back to ${dynamicBackLabel}`,
|
|
103291
|
+
children: [
|
|
103292
|
+
/* @__PURE__ */ jsxs(
|
|
103293
|
+
"svg",
|
|
103294
|
+
{
|
|
103295
|
+
width: "16",
|
|
103296
|
+
height: "16",
|
|
103297
|
+
viewBox: "0 0 24 24",
|
|
103298
|
+
fill: "none",
|
|
103299
|
+
stroke: "currentColor",
|
|
103300
|
+
strokeWidth: "2.5",
|
|
103301
|
+
strokeLinecap: "round",
|
|
103302
|
+
strokeLinejoin: "round",
|
|
103303
|
+
className: "group-hover:-translate-x-0.5 transition-transform text-neutral-400 group-hover:text-white",
|
|
103304
|
+
children: [
|
|
103305
|
+
/* @__PURE__ */ jsx("line", { x1: "19", y1: "12", x2: "5", y2: "12" }),
|
|
103306
|
+
/* @__PURE__ */ jsx("polyline", { points: "12 19 5 12 12 5" })
|
|
103307
|
+
]
|
|
103308
|
+
}
|
|
103309
|
+
),
|
|
103310
|
+
/* @__PURE__ */ jsx("span", { className: "capitalize truncate max-w-[240px]", children: dynamicBackLabel })
|
|
103311
|
+
]
|
|
103312
|
+
}
|
|
103313
|
+
) }),
|
|
103314
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-neutral-800 px-3 py-2 rounded flex items-center gap-2 mb-4 mx-4 relative focus-within:ring-1 focus-within:ring-neutral-600", children: [
|
|
103162
103315
|
/* @__PURE__ */ jsxs(
|
|
103163
103316
|
"svg",
|
|
103164
103317
|
{
|
|
@@ -103166,7 +103319,8 @@ function AllElements({
|
|
|
103166
103319
|
stroke: "currentColor",
|
|
103167
103320
|
fill: "none",
|
|
103168
103321
|
viewBox: "0 0 24 24",
|
|
103169
|
-
className: "text-gray-
|
|
103322
|
+
className: "text-gray-400 shrink-0 cursor-pointer hover:text-white transition-colors",
|
|
103323
|
+
onClick: handleSearchSubmit,
|
|
103170
103324
|
children: [
|
|
103171
103325
|
/* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
|
|
103172
103326
|
/* @__PURE__ */ jsx("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })
|
|
@@ -103178,9 +103332,10 @@ function AllElements({
|
|
|
103178
103332
|
{
|
|
103179
103333
|
type: "text",
|
|
103180
103334
|
value: search,
|
|
103181
|
-
onChange:
|
|
103182
|
-
onKeyDown:
|
|
103183
|
-
|
|
103335
|
+
onChange: (e3) => setSearch(e3.target.value),
|
|
103336
|
+
onKeyDown: handleKeyDown,
|
|
103337
|
+
placeholder: title ? `Search in ${title}...` : "Search elements & shapes...",
|
|
103338
|
+
className: "bg-transparent outline-none text-sm text-gray-200 placeholder-gray-500 w-full pr-8"
|
|
103184
103339
|
}
|
|
103185
103340
|
),
|
|
103186
103341
|
search && /* @__PURE__ */ jsx(
|
|
@@ -103188,13 +103343,13 @@ function AllElements({
|
|
|
103188
103343
|
{
|
|
103189
103344
|
type: "button",
|
|
103190
103345
|
onClick: clearSearch,
|
|
103191
|
-
className: "absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-white transition-colors p-1",
|
|
103346
|
+
className: "absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-white transition-colors p-1 cursor-pointer",
|
|
103192
103347
|
"aria-label": "Clear search",
|
|
103193
103348
|
children: /* @__PURE__ */ jsxs(
|
|
103194
103349
|
"svg",
|
|
103195
103350
|
{
|
|
103196
|
-
width: "
|
|
103197
|
-
height: "
|
|
103351
|
+
width: "14",
|
|
103352
|
+
height: "14",
|
|
103198
103353
|
viewBox: "0 0 24 24",
|
|
103199
103354
|
fill: "none",
|
|
103200
103355
|
stroke: "currentColor",
|
|
@@ -103208,9 +103363,20 @@ function AllElements({
|
|
|
103208
103363
|
}
|
|
103209
103364
|
)
|
|
103210
103365
|
] }),
|
|
103211
|
-
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px]" }),
|
|
103212
|
-
/* @__PURE__ */ jsx("div", { className: "h-
|
|
103213
|
-
/* @__PURE__ */ jsx("
|
|
103366
|
+
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px] border-neutral-800" }),
|
|
103367
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-auto custom-scrollbar", ref: scrollRef, children: /* @__PURE__ */ jsx("div", { className: "mx-4 py-5", children: elements2.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "py-12 text-center", children: [
|
|
103368
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-400 text-sm", children: search.trim() ? `No results found for "${search}"` : "No elements found" }),
|
|
103369
|
+
search.trim() && /* @__PURE__ */ jsx(
|
|
103370
|
+
"button",
|
|
103371
|
+
{
|
|
103372
|
+
type: "button",
|
|
103373
|
+
onClick: clearSearch,
|
|
103374
|
+
className: "mt-3 text-xs text-emerald-400 hover:underline cursor-pointer",
|
|
103375
|
+
children: "Clear search"
|
|
103376
|
+
}
|
|
103377
|
+
)
|
|
103378
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
103379
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 md:grid-cols-3 gap-3", children: elements2.map((shape, i) => /* @__PURE__ */ jsx(
|
|
103214
103380
|
"button",
|
|
103215
103381
|
{
|
|
103216
103382
|
type: "button",
|
|
@@ -103225,21 +103391,22 @@ function AllElements({
|
|
|
103225
103391
|
useSidePanelStore.getState().setActivePanel(null);
|
|
103226
103392
|
}
|
|
103227
103393
|
},
|
|
103228
|
-
className: "w-full cursor-pointer flex items-center justify-center",
|
|
103394
|
+
className: "w-full cursor-pointer flex items-center justify-center p-2 rounded-lg bg-neutral-900/40 hover:bg-neutral-800 border border-neutral-800/60 hover:border-neutral-700 transition-colors",
|
|
103229
103395
|
children: shape.image && /* @__PURE__ */ jsx(
|
|
103230
103396
|
"img",
|
|
103231
103397
|
{
|
|
103232
103398
|
src: shape.image,
|
|
103233
|
-
alt: shape.category,
|
|
103399
|
+
alt: shape.category || "element",
|
|
103234
103400
|
width: 150,
|
|
103235
103401
|
height: 150,
|
|
103236
|
-
className: "w-full h-
|
|
103402
|
+
className: "w-full h-20 object-contain",
|
|
103403
|
+
loading: "lazy"
|
|
103237
103404
|
}
|
|
103238
103405
|
)
|
|
103239
103406
|
},
|
|
103240
|
-
i
|
|
103407
|
+
shape._id || shape.image || i
|
|
103241
103408
|
)) }),
|
|
103242
|
-
isLoading && /* @__PURE__ */ jsx("div", { className: "text-center py-4 text-
|
|
103409
|
+
isLoading && /* @__PURE__ */ jsx("div", { className: "text-center py-4 text-xs text-gray-400 animate-pulse", children: "Loading more elements..." })
|
|
103243
103410
|
] }) }) })
|
|
103244
103411
|
] });
|
|
103245
103412
|
}
|
|
@@ -103272,72 +103439,181 @@ function ElementsPanel() {
|
|
|
103272
103439
|
const [page, setPage] = useState(1);
|
|
103273
103440
|
const [category, setCategory] = useState(null);
|
|
103274
103441
|
const [keyType, setKeyType] = useState(null);
|
|
103275
|
-
const [
|
|
103442
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
103443
|
+
const [mainSearch, setMainSearch] = useState("");
|
|
103444
|
+
const [isViewingAll, setIsViewingAll] = useState(false);
|
|
103445
|
+
const [title, setTitle] = useState("");
|
|
103276
103446
|
const [hasMore, setHasMore] = useState(true);
|
|
103277
103447
|
const scrollRef = useRef(null);
|
|
103278
|
-
const getSVG = async (type = null, categoryValue, pageNo = 1) => {
|
|
103448
|
+
const getSVG = async (type = null, categoryValue = null, searchVal = null, pageNo = 1) => {
|
|
103279
103449
|
startLoading();
|
|
103280
103450
|
if (pageNo === 1) {
|
|
103281
103451
|
setHasMore(true);
|
|
103282
103452
|
setPage(1);
|
|
103283
103453
|
setCategory(categoryValue);
|
|
103454
|
+
setKeyType(type);
|
|
103455
|
+
setSearchQuery(searchVal || "");
|
|
103456
|
+
}
|
|
103457
|
+
if (categoryValue === "Recent items") {
|
|
103458
|
+
const q2 = (searchVal || "").toLowerCase().trim();
|
|
103459
|
+
const filtered = q2 ? recentItems.filter(
|
|
103460
|
+
(item) => item.category?.toLowerCase().includes(q2) || item.tags?.some((t2) => t2.toLowerCase().includes(q2))
|
|
103461
|
+
) : recentItems;
|
|
103462
|
+
setElementsSVG(filtered);
|
|
103463
|
+
setHasMore(false);
|
|
103464
|
+
stopLoading();
|
|
103465
|
+
return;
|
|
103284
103466
|
}
|
|
103285
|
-
|
|
103286
|
-
|
|
103287
|
-
|
|
103288
|
-
|
|
103289
|
-
|
|
103290
|
-
|
|
103291
|
-
|
|
103292
|
-
|
|
103293
|
-
|
|
103294
|
-
|
|
103295
|
-
|
|
103296
|
-
|
|
103297
|
-
|
|
103467
|
+
try {
|
|
103468
|
+
const request = type ? ShapeService.getAllExternal({
|
|
103469
|
+
isVector: true,
|
|
103470
|
+
page: pageNo,
|
|
103471
|
+
limit: 30,
|
|
103472
|
+
keyType: type,
|
|
103473
|
+
query: searchVal || categoryValue || null
|
|
103474
|
+
}) : ShapeService.getAll({
|
|
103475
|
+
category: categoryValue || null,
|
|
103476
|
+
search: searchVal || null,
|
|
103477
|
+
page: pageNo,
|
|
103478
|
+
limit: 30
|
|
103479
|
+
});
|
|
103480
|
+
const res = await request;
|
|
103481
|
+
const items = res.items || [];
|
|
103298
103482
|
const newItems = items.filter((x2) => x2.status && x2.image);
|
|
103299
103483
|
setElementsSVG(
|
|
103300
103484
|
(prev) => pageNo === 1 ? newItems : [...prev, ...newItems]
|
|
103301
103485
|
);
|
|
103302
103486
|
if (newItems.length < 20) setHasMore(false);
|
|
103303
|
-
|
|
103487
|
+
else setHasMore(true);
|
|
103488
|
+
} catch (err) {
|
|
103489
|
+
console.error("Failed to load elements:", err);
|
|
103490
|
+
if (pageNo === 1) setElementsSVG([]);
|
|
103491
|
+
setHasMore(false);
|
|
103492
|
+
} finally {
|
|
103493
|
+
stopLoading();
|
|
103494
|
+
}
|
|
103304
103495
|
};
|
|
103305
103496
|
useEffect(() => {
|
|
103306
103497
|
const el = scrollRef.current;
|
|
103307
|
-
if (!el) return;
|
|
103498
|
+
if (!el || !isViewingAll) return;
|
|
103308
103499
|
const onScroll = () => {
|
|
103309
103500
|
if (!hasMore || isLoading) return;
|
|
103310
103501
|
if (el.scrollTop + el.clientHeight >= el.scrollHeight - 200) {
|
|
103311
|
-
|
|
103312
|
-
|
|
103313
|
-
|
|
103502
|
+
setPage((prev) => {
|
|
103503
|
+
const next = prev + 1;
|
|
103504
|
+
getSVG(keyType, category, searchQuery, next);
|
|
103505
|
+
return next;
|
|
103506
|
+
});
|
|
103314
103507
|
}
|
|
103315
103508
|
};
|
|
103316
103509
|
el.addEventListener("scroll", onScroll);
|
|
103317
103510
|
return () => el.removeEventListener("scroll", onScroll);
|
|
103318
|
-
}, [hasMore, isLoading]);
|
|
103319
|
-
|
|
103511
|
+
}, [hasMore, isLoading, isViewingAll, keyType, category, searchQuery]);
|
|
103512
|
+
const handleMainSearch = (val) => {
|
|
103513
|
+
const q2 = val.trim();
|
|
103514
|
+
if (!q2) return;
|
|
103515
|
+
setIsViewingAll(true);
|
|
103516
|
+
setTitle(`Search: "${q2}"`);
|
|
103517
|
+
getSVG(null, null, q2, 1);
|
|
103518
|
+
};
|
|
103519
|
+
const handleBack = () => {
|
|
103520
|
+
if (searchQuery && category && category !== "Recent items") {
|
|
103521
|
+
setSearchQuery("");
|
|
103522
|
+
setPage(1);
|
|
103523
|
+
setTitle(category);
|
|
103524
|
+
getSVG(keyType, category, "", 1);
|
|
103525
|
+
return;
|
|
103526
|
+
}
|
|
103527
|
+
setIsViewingAll(false);
|
|
103528
|
+
setElementsSVG([]);
|
|
103529
|
+
setCategory(null);
|
|
103530
|
+
setKeyType(null);
|
|
103531
|
+
setSearchQuery("");
|
|
103532
|
+
setMainSearch("");
|
|
103533
|
+
setTitle("");
|
|
103534
|
+
setPage(1);
|
|
103535
|
+
setHasMore(true);
|
|
103536
|
+
};
|
|
103537
|
+
if (isViewingAll) {
|
|
103320
103538
|
return /* @__PURE__ */ jsx(
|
|
103321
103539
|
AllElements,
|
|
103322
103540
|
{
|
|
103323
103541
|
elements: elementsSVG,
|
|
103324
103542
|
scrollRef,
|
|
103543
|
+
title,
|
|
103544
|
+
backText: title || "Elements",
|
|
103545
|
+
initialSearch: searchQuery,
|
|
103325
103546
|
onSearch: (value) => {
|
|
103326
|
-
|
|
103327
|
-
|
|
103328
|
-
|
|
103547
|
+
if (value.trim()) {
|
|
103548
|
+
setTitle(category ? `${category} - "${value.trim()}"` : `Search: "${value.trim()}"`);
|
|
103549
|
+
} else {
|
|
103550
|
+
setTitle(
|
|
103551
|
+
category || (keyType === "pixabay" ? "Pixabay Vectors" : keyType === "flat" ? "FlatIcon Icons" : "All Elements")
|
|
103552
|
+
);
|
|
103553
|
+
}
|
|
103554
|
+
getSVG(keyType, category, value, 1);
|
|
103329
103555
|
},
|
|
103330
|
-
onBack:
|
|
103331
|
-
setElementsSVG([]);
|
|
103332
|
-
setPage(1);
|
|
103333
|
-
setCategory(null);
|
|
103334
|
-
setKeyType(null);
|
|
103335
|
-
setHasMore(true);
|
|
103336
|
-
}
|
|
103556
|
+
onBack: handleBack
|
|
103337
103557
|
}
|
|
103338
103558
|
);
|
|
103339
103559
|
}
|
|
103340
103560
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
103561
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-neutral-800 px-3 py-2 rounded flex items-center gap-2 mb-4 mx-4 relative focus-within:ring-1 focus-within:ring-neutral-600", children: [
|
|
103562
|
+
/* @__PURE__ */ jsxs(
|
|
103563
|
+
"svg",
|
|
103564
|
+
{
|
|
103565
|
+
width: "18",
|
|
103566
|
+
stroke: "currentColor",
|
|
103567
|
+
fill: "none",
|
|
103568
|
+
viewBox: "0 0 24 24",
|
|
103569
|
+
className: "text-gray-400 shrink-0",
|
|
103570
|
+
children: [
|
|
103571
|
+
/* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
|
|
103572
|
+
/* @__PURE__ */ jsx("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })
|
|
103573
|
+
]
|
|
103574
|
+
}
|
|
103575
|
+
),
|
|
103576
|
+
/* @__PURE__ */ jsx(
|
|
103577
|
+
"input",
|
|
103578
|
+
{
|
|
103579
|
+
type: "text",
|
|
103580
|
+
placeholder: "Search elements & shapes...",
|
|
103581
|
+
value: mainSearch,
|
|
103582
|
+
onChange: (e3) => setMainSearch(e3.target.value),
|
|
103583
|
+
onKeyDown: (e3) => {
|
|
103584
|
+
e3.stopPropagation();
|
|
103585
|
+
if (e3.key === "Enter" && mainSearch.trim()) {
|
|
103586
|
+
handleMainSearch(mainSearch.trim());
|
|
103587
|
+
}
|
|
103588
|
+
},
|
|
103589
|
+
className: "bg-transparent outline-none text-sm text-gray-200 placeholder-gray-500 w-full pr-7"
|
|
103590
|
+
}
|
|
103591
|
+
),
|
|
103592
|
+
mainSearch && /* @__PURE__ */ jsx(
|
|
103593
|
+
"button",
|
|
103594
|
+
{
|
|
103595
|
+
type: "button",
|
|
103596
|
+
onClick: () => setMainSearch(""),
|
|
103597
|
+
className: "absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-white transition-colors p-1 cursor-pointer",
|
|
103598
|
+
"aria-label": "Clear search",
|
|
103599
|
+
children: /* @__PURE__ */ jsxs(
|
|
103600
|
+
"svg",
|
|
103601
|
+
{
|
|
103602
|
+
width: "14",
|
|
103603
|
+
height: "14",
|
|
103604
|
+
viewBox: "0 0 24 24",
|
|
103605
|
+
fill: "none",
|
|
103606
|
+
stroke: "currentColor",
|
|
103607
|
+
strokeWidth: "2",
|
|
103608
|
+
children: [
|
|
103609
|
+
/* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
103610
|
+
/* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
103611
|
+
]
|
|
103612
|
+
}
|
|
103613
|
+
)
|
|
103614
|
+
}
|
|
103615
|
+
)
|
|
103616
|
+
] }),
|
|
103341
103617
|
/* @__PURE__ */ jsxs("div", { className: "pb-5 mx-4", children: [
|
|
103342
103618
|
/* @__PURE__ */ jsx("h2", { className: "text-md font-medium mb-3", children: "Upload From Other Media" }),
|
|
103343
103619
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
@@ -103345,11 +103621,13 @@ function ElementsPanel() {
|
|
|
103345
103621
|
"button",
|
|
103346
103622
|
{
|
|
103347
103623
|
type: "button",
|
|
103348
|
-
className: "cursor-pointer",
|
|
103624
|
+
className: "cursor-pointer hover:opacity-80 transition-opacity",
|
|
103349
103625
|
onClick: () => {
|
|
103350
|
-
|
|
103351
|
-
|
|
103626
|
+
setIsViewingAll(true);
|
|
103627
|
+
setTitle("Pixabay Vectors");
|
|
103628
|
+
getSVG("pixabay", "", "", 1);
|
|
103352
103629
|
},
|
|
103630
|
+
title: "Search Pixabay Vectors",
|
|
103353
103631
|
children: /* @__PURE__ */ jsx(PixabayIcon, {})
|
|
103354
103632
|
}
|
|
103355
103633
|
),
|
|
@@ -103357,11 +103635,13 @@ function ElementsPanel() {
|
|
|
103357
103635
|
"button",
|
|
103358
103636
|
{
|
|
103359
103637
|
type: "button",
|
|
103360
|
-
className: "cursor-pointer",
|
|
103638
|
+
className: "cursor-pointer hover:opacity-80 transition-opacity",
|
|
103361
103639
|
onClick: () => {
|
|
103362
|
-
|
|
103363
|
-
|
|
103640
|
+
setIsViewingAll(true);
|
|
103641
|
+
setTitle("FlatIcon Icons");
|
|
103642
|
+
getSVG("flat", "", "", 1);
|
|
103364
103643
|
},
|
|
103644
|
+
title: "Search FlatIcon Icons",
|
|
103365
103645
|
children: /* @__PURE__ */ jsx(
|
|
103366
103646
|
"img",
|
|
103367
103647
|
{
|
|
@@ -103376,20 +103656,21 @@ function ElementsPanel() {
|
|
|
103376
103656
|
)
|
|
103377
103657
|
] })
|
|
103378
103658
|
] }),
|
|
103379
|
-
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px]" }),
|
|
103380
|
-
/* @__PURE__ */ jsxs("div", { className: "h-[calc(100dvh-
|
|
103659
|
+
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px] border-neutral-800" }),
|
|
103660
|
+
/* @__PURE__ */ jsxs("div", { className: "h-[calc(100dvh-200px)] overflow-auto custom-scrollbar", children: [
|
|
103381
103661
|
recentItems.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mx-4", children: [
|
|
103382
103662
|
/* @__PURE__ */ jsxs("div", { className: "py-5", children: [
|
|
103383
103663
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
103384
103664
|
/* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold mb-3", children: "Recent items" }),
|
|
103385
103665
|
recentItems.length > 3 && /* @__PURE__ */ jsx(
|
|
103386
|
-
"
|
|
103666
|
+
"button",
|
|
103387
103667
|
{
|
|
103388
|
-
|
|
103668
|
+
type: "button",
|
|
103669
|
+
className: "underline text-xs text-neutral-400 hover:text-white cursor-pointer",
|
|
103389
103670
|
onClick: () => {
|
|
103390
|
-
|
|
103391
|
-
|
|
103392
|
-
|
|
103671
|
+
setIsViewingAll(true);
|
|
103672
|
+
setTitle("Recent items");
|
|
103673
|
+
getSVG(null, "Recent items", "", 1);
|
|
103393
103674
|
},
|
|
103394
103675
|
children: "View all"
|
|
103395
103676
|
}
|
|
@@ -103398,36 +103679,45 @@ function ElementsPanel() {
|
|
|
103398
103679
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 md:grid-cols-3 gap-3", children: recentItems.slice(0, 3).map((shape, i) => /* @__PURE__ */ jsx(
|
|
103399
103680
|
"button",
|
|
103400
103681
|
{
|
|
103682
|
+
type: "button",
|
|
103401
103683
|
onClick: () => {
|
|
103402
103684
|
addSVGToCanvas("svg", shape.image);
|
|
103403
103685
|
addRecentItem(shape);
|
|
103686
|
+
if (window.innerWidth < 768) {
|
|
103687
|
+
useSidePanelStore.getState().setActivePanel(null);
|
|
103688
|
+
}
|
|
103404
103689
|
},
|
|
103405
|
-
className: "w-full cursor-pointer flex items-center justify-center",
|
|
103690
|
+
className: "w-full cursor-pointer flex items-center justify-center p-2 rounded-lg bg-neutral-900/40 hover:bg-neutral-800 border border-neutral-800/60 hover:border-neutral-700 transition-colors",
|
|
103406
103691
|
children: /* @__PURE__ */ jsx(
|
|
103407
103692
|
"img",
|
|
103408
103693
|
{
|
|
103409
103694
|
src: shape.image,
|
|
103410
|
-
alt: shape.category,
|
|
103695
|
+
alt: shape.category || "recent element",
|
|
103411
103696
|
width: 150,
|
|
103412
103697
|
height: 150,
|
|
103413
|
-
className: "w-full h-
|
|
103698
|
+
className: "w-full h-20 object-contain"
|
|
103414
103699
|
}
|
|
103415
103700
|
)
|
|
103416
103701
|
},
|
|
103417
103702
|
shape._id || shape.image || i
|
|
103418
103703
|
)) })
|
|
103419
103704
|
] }),
|
|
103420
|
-
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px]" })
|
|
103705
|
+
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px] border-neutral-800" })
|
|
103421
103706
|
] }),
|
|
103422
103707
|
elements.map((item, i) => /* @__PURE__ */ jsxs("div", { className: "mx-4", children: [
|
|
103423
103708
|
/* @__PURE__ */ jsxs("div", { className: "py-5", children: [
|
|
103424
103709
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
103425
103710
|
/* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold mb-3", children: item.category }),
|
|
103426
103711
|
/* @__PURE__ */ jsx(
|
|
103427
|
-
"
|
|
103712
|
+
"button",
|
|
103428
103713
|
{
|
|
103429
|
-
|
|
103430
|
-
|
|
103714
|
+
type: "button",
|
|
103715
|
+
className: "underline text-xs text-neutral-400 hover:text-white cursor-pointer",
|
|
103716
|
+
onClick: () => {
|
|
103717
|
+
setIsViewingAll(true);
|
|
103718
|
+
setTitle(item.category);
|
|
103719
|
+
getSVG(null, item.category, "", 1);
|
|
103720
|
+
},
|
|
103431
103721
|
children: "View all"
|
|
103432
103722
|
}
|
|
103433
103723
|
)
|
|
@@ -103435,6 +103725,7 @@ function ElementsPanel() {
|
|
|
103435
103725
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 md:grid-cols-3 gap-3", children: item.data.map((shape, j2) => /* @__PURE__ */ jsx(
|
|
103436
103726
|
"button",
|
|
103437
103727
|
{
|
|
103728
|
+
type: "button",
|
|
103438
103729
|
onClick: () => {
|
|
103439
103730
|
addSVGToCanvas("svg", shape.image);
|
|
103440
103731
|
addRecentItem({
|
|
@@ -103445,8 +103736,11 @@ function ElementsPanel() {
|
|
|
103445
103736
|
largeImage: shape.image,
|
|
103446
103737
|
tags: []
|
|
103447
103738
|
});
|
|
103739
|
+
if (window.innerWidth < 768) {
|
|
103740
|
+
useSidePanelStore.getState().setActivePanel(null);
|
|
103741
|
+
}
|
|
103448
103742
|
},
|
|
103449
|
-
className: "w-full cursor-pointer flex items-center justify-center",
|
|
103743
|
+
className: "w-full cursor-pointer flex items-center justify-center p-2 rounded-lg bg-neutral-900/40 hover:bg-neutral-800 border border-neutral-800/60 hover:border-neutral-700 transition-colors",
|
|
103450
103744
|
children: /* @__PURE__ */ jsx(
|
|
103451
103745
|
"img",
|
|
103452
103746
|
{
|
|
@@ -103454,14 +103748,15 @@ function ElementsPanel() {
|
|
|
103454
103748
|
alt: item.category,
|
|
103455
103749
|
width: 150,
|
|
103456
103750
|
height: 150,
|
|
103457
|
-
className: "w-full h-
|
|
103751
|
+
className: "w-full h-20 object-contain",
|
|
103752
|
+
loading: "lazy"
|
|
103458
103753
|
}
|
|
103459
103754
|
)
|
|
103460
103755
|
},
|
|
103461
103756
|
j2
|
|
103462
103757
|
)) })
|
|
103463
103758
|
] }),
|
|
103464
|
-
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px]" })
|
|
103759
|
+
/* @__PURE__ */ jsx("hr", { className: "text-neutral-800 h-[0.5px] border-neutral-800" })
|
|
103465
103760
|
] }, i))
|
|
103466
103761
|
] })
|
|
103467
103762
|
] });
|
|
@@ -158363,6 +158658,20 @@ function DuplicateButton() {
|
|
|
158363
158658
|
const duplicate = async () => {
|
|
158364
158659
|
const active = canvas2.getActiveObject();
|
|
158365
158660
|
if (!active) return;
|
|
158661
|
+
const currentSide = useHistoryStore.getState().currentPage;
|
|
158662
|
+
const ensureTextLoaded = async (obj) => {
|
|
158663
|
+
if (obj.type === "textbox" || obj.type === "text" || obj.type === "i-text") {
|
|
158664
|
+
if (obj.fontFamily) {
|
|
158665
|
+
try {
|
|
158666
|
+
await document.fonts.load(`16px "${obj.fontFamily}"`);
|
|
158667
|
+
} catch {
|
|
158668
|
+
}
|
|
158669
|
+
}
|
|
158670
|
+
obj.initDimensions?.();
|
|
158671
|
+
obj.set("dirty", true);
|
|
158672
|
+
}
|
|
158673
|
+
obj.__pageSide = currentSide;
|
|
158674
|
+
};
|
|
158366
158675
|
if (active.type?.toLowerCase() === "activeselection") {
|
|
158367
158676
|
const selection = active;
|
|
158368
158677
|
const originals = selection.getObjects().filter((o2) => o2.name !== "workarea");
|
|
@@ -158370,13 +158679,14 @@ function DuplicateButton() {
|
|
|
158370
158679
|
canvas2.discardActiveObject();
|
|
158371
158680
|
const clones = await Promise.all(
|
|
158372
158681
|
originals.map(async (obj) => {
|
|
158373
|
-
const cloned2 = await obj.clone();
|
|
158682
|
+
const cloned2 = await obj.clone(HISTORY_CUSTOM_PROPERTIES);
|
|
158374
158683
|
cloned2.set({
|
|
158375
158684
|
left: (obj.left ?? 0) + OFFSET$1,
|
|
158376
158685
|
top: (obj.top ?? 0) + OFFSET$1,
|
|
158377
158686
|
selectable: true,
|
|
158378
158687
|
evented: true
|
|
158379
158688
|
});
|
|
158689
|
+
await ensureTextLoaded(cloned2);
|
|
158380
158690
|
canvas2.add(cloned2);
|
|
158381
158691
|
return cloned2;
|
|
158382
158692
|
})
|
|
@@ -158389,18 +158699,21 @@ function DuplicateButton() {
|
|
|
158389
158699
|
return;
|
|
158390
158700
|
}
|
|
158391
158701
|
if (active.name === "workarea") return;
|
|
158392
|
-
const cloned = await active.clone();
|
|
158702
|
+
const cloned = await active.clone(HISTORY_CUSTOM_PROPERTIES);
|
|
158393
158703
|
cloned.set({
|
|
158394
158704
|
left: (active.left ?? 0) + OFFSET$1,
|
|
158395
158705
|
top: (active.top ?? 0) + OFFSET$1,
|
|
158396
158706
|
selectable: true,
|
|
158397
158707
|
evented: true
|
|
158398
158708
|
});
|
|
158709
|
+
await ensureTextLoaded(cloned);
|
|
158710
|
+
cloned.setCoords();
|
|
158399
158711
|
canvas2.discardActiveObject();
|
|
158400
158712
|
canvas2.add(cloned);
|
|
158401
158713
|
canvas2.setActiveObject(cloned);
|
|
158402
158714
|
bringOverlaysToFront(canvas2);
|
|
158403
158715
|
canvas2.requestRenderAll();
|
|
158716
|
+
saveState();
|
|
158404
158717
|
};
|
|
158405
158718
|
return /* @__PURE__ */ jsx(
|
|
158406
158719
|
"button",
|
|
@@ -160320,6 +160633,175 @@ function createWorkarea(canvas2, container, productData, pageSide) {
|
|
|
160320
160633
|
createOverlays(canvas2, workarea, currentSide);
|
|
160321
160634
|
return workarea;
|
|
160322
160635
|
}
|
|
160636
|
+
const SNAP_THRESHOLD_PX = 7;
|
|
160637
|
+
const GUIDELINE_COLOR = "#28a128";
|
|
160638
|
+
const GUIDELINE_WIDTH = 1.2;
|
|
160639
|
+
let activeVLine = null;
|
|
160640
|
+
let activeHLine = null;
|
|
160641
|
+
function clearAlignmentGuidelines(canvas2) {
|
|
160642
|
+
if (!canvas2) return;
|
|
160643
|
+
let changed = false;
|
|
160644
|
+
if (activeVLine) {
|
|
160645
|
+
canvas2.remove(activeVLine);
|
|
160646
|
+
activeVLine = null;
|
|
160647
|
+
changed = true;
|
|
160648
|
+
}
|
|
160649
|
+
if (activeHLine) {
|
|
160650
|
+
canvas2.remove(activeHLine);
|
|
160651
|
+
activeHLine = null;
|
|
160652
|
+
changed = true;
|
|
160653
|
+
}
|
|
160654
|
+
canvas2.getObjects().forEach((o2) => {
|
|
160655
|
+
if (o2.name === "guideline-alignment-v" || o2.name === "guideline-alignment-h") {
|
|
160656
|
+
canvas2.remove(o2);
|
|
160657
|
+
changed = true;
|
|
160658
|
+
}
|
|
160659
|
+
});
|
|
160660
|
+
if (changed) {
|
|
160661
|
+
canvas2.requestRenderAll();
|
|
160662
|
+
}
|
|
160663
|
+
}
|
|
160664
|
+
function handleObjectAlignmentSnap(canvas2, target) {
|
|
160665
|
+
if (!canvas2 || !target) return;
|
|
160666
|
+
const targetName = target.name || "";
|
|
160667
|
+
if (targetName === "workarea" || targetName === "bg-image" || targetName === "canvas-grid" || targetName.startsWith("overlay-") || targetName.startsWith("guideline-")) {
|
|
160668
|
+
return;
|
|
160669
|
+
}
|
|
160670
|
+
const workarea = canvas2.getObjects().find((o2) => o2.name === "workarea");
|
|
160671
|
+
if (!workarea) return;
|
|
160672
|
+
const zoom = canvas2.getZoom() || 1;
|
|
160673
|
+
const threshold = SNAP_THRESHOLD_PX / zoom;
|
|
160674
|
+
const waL = workarea.left ?? 0;
|
|
160675
|
+
const waT = workarea.top ?? 0;
|
|
160676
|
+
const waW = workarea.getScaledWidth();
|
|
160677
|
+
const waH = workarea.getScaledHeight();
|
|
160678
|
+
const waCenterX = waL + waW / 2;
|
|
160679
|
+
const waCenterY = waT + waH / 2;
|
|
160680
|
+
const targetCenter = target.getCenterPoint();
|
|
160681
|
+
target.getBoundingRect();
|
|
160682
|
+
const curLeft = target.left ?? 0;
|
|
160683
|
+
target.top ?? 0;
|
|
160684
|
+
let snappedX = false;
|
|
160685
|
+
let snappedY = false;
|
|
160686
|
+
let snapLineX = waCenterX;
|
|
160687
|
+
let snapLineY = waCenterY;
|
|
160688
|
+
const distCenterX = Math.abs(targetCenter.x - waCenterX);
|
|
160689
|
+
if (distCenterX <= threshold) {
|
|
160690
|
+
const diffX = waCenterX - targetCenter.x;
|
|
160691
|
+
target.set("left", curLeft + diffX);
|
|
160692
|
+
target.setCoords();
|
|
160693
|
+
snappedX = true;
|
|
160694
|
+
snapLineX = waCenterX;
|
|
160695
|
+
} else {
|
|
160696
|
+
const otherObjects = canvas2.getObjects().filter(
|
|
160697
|
+
(o2) => o2 !== target && o2.selectable !== false && o2.name !== "workarea" && o2.name !== "bg-image" && !o2.name?.startsWith("overlay-") && !o2.name?.startsWith("guideline-") && o2.name !== "canvas-grid"
|
|
160698
|
+
);
|
|
160699
|
+
for (const other of otherObjects) {
|
|
160700
|
+
const otherCenter = other.getCenterPoint();
|
|
160701
|
+
if (Math.abs(targetCenter.x - otherCenter.x) <= threshold) {
|
|
160702
|
+
const diffX = otherCenter.x - targetCenter.x;
|
|
160703
|
+
target.set("left", curLeft + diffX);
|
|
160704
|
+
target.setCoords();
|
|
160705
|
+
snappedX = true;
|
|
160706
|
+
snapLineX = otherCenter.x;
|
|
160707
|
+
break;
|
|
160708
|
+
}
|
|
160709
|
+
}
|
|
160710
|
+
}
|
|
160711
|
+
const updatedCenter = target.getCenterPoint();
|
|
160712
|
+
const distCenterY = Math.abs(updatedCenter.y - waCenterY);
|
|
160713
|
+
if (distCenterY <= threshold) {
|
|
160714
|
+
const diffY = waCenterY - updatedCenter.y;
|
|
160715
|
+
target.set("top", (target.top ?? 0) + diffY);
|
|
160716
|
+
target.setCoords();
|
|
160717
|
+
snappedY = true;
|
|
160718
|
+
snapLineY = waCenterY;
|
|
160719
|
+
} else {
|
|
160720
|
+
const otherObjects = canvas2.getObjects().filter(
|
|
160721
|
+
(o2) => o2 !== target && o2.selectable !== false && o2.name !== "workarea" && o2.name !== "bg-image" && !o2.name?.startsWith("overlay-") && !o2.name?.startsWith("guideline-") && o2.name !== "canvas-grid"
|
|
160722
|
+
);
|
|
160723
|
+
for (const other of otherObjects) {
|
|
160724
|
+
const otherCenter = other.getCenterPoint();
|
|
160725
|
+
if (Math.abs(updatedCenter.y - otherCenter.y) <= threshold) {
|
|
160726
|
+
const diffY = otherCenter.y - updatedCenter.y;
|
|
160727
|
+
target.set("top", (target.top ?? 0) + diffY);
|
|
160728
|
+
target.setCoords();
|
|
160729
|
+
snappedY = true;
|
|
160730
|
+
snapLineY = otherCenter.y;
|
|
160731
|
+
break;
|
|
160732
|
+
}
|
|
160733
|
+
}
|
|
160734
|
+
}
|
|
160735
|
+
if (snappedX) {
|
|
160736
|
+
const y1 = waT;
|
|
160737
|
+
const y2 = waT + waH;
|
|
160738
|
+
if (!activeVLine) {
|
|
160739
|
+
activeVLine = new vo([snapLineX, y1, snapLineX, y2], {
|
|
160740
|
+
name: "guideline-alignment-v",
|
|
160741
|
+
stroke: GUIDELINE_COLOR,
|
|
160742
|
+
strokeWidth: GUIDELINE_WIDTH / zoom,
|
|
160743
|
+
strokeUniform: true,
|
|
160744
|
+
selectable: false,
|
|
160745
|
+
evented: false,
|
|
160746
|
+
excludeFromExport: true,
|
|
160747
|
+
hasControls: false,
|
|
160748
|
+
hasBorders: false,
|
|
160749
|
+
lockMovementX: true,
|
|
160750
|
+
lockMovementY: true
|
|
160751
|
+
});
|
|
160752
|
+
canvas2.add(activeVLine);
|
|
160753
|
+
} else {
|
|
160754
|
+
activeVLine.set({
|
|
160755
|
+
x1: snapLineX,
|
|
160756
|
+
y1,
|
|
160757
|
+
x2: snapLineX,
|
|
160758
|
+
y2,
|
|
160759
|
+
strokeWidth: GUIDELINE_WIDTH / zoom,
|
|
160760
|
+
visible: true
|
|
160761
|
+
});
|
|
160762
|
+
activeVLine.setCoords();
|
|
160763
|
+
}
|
|
160764
|
+
canvas2.bringObjectToFront(activeVLine);
|
|
160765
|
+
} else if (activeVLine) {
|
|
160766
|
+
canvas2.remove(activeVLine);
|
|
160767
|
+
activeVLine = null;
|
|
160768
|
+
}
|
|
160769
|
+
if (snappedY) {
|
|
160770
|
+
const x1 = waL;
|
|
160771
|
+
const x2 = waL + waW;
|
|
160772
|
+
if (!activeHLine) {
|
|
160773
|
+
activeHLine = new vo([x1, snapLineY, x2, snapLineY], {
|
|
160774
|
+
name: "guideline-alignment-h",
|
|
160775
|
+
stroke: GUIDELINE_COLOR,
|
|
160776
|
+
strokeWidth: GUIDELINE_WIDTH / zoom,
|
|
160777
|
+
strokeUniform: true,
|
|
160778
|
+
selectable: false,
|
|
160779
|
+
evented: false,
|
|
160780
|
+
excludeFromExport: true,
|
|
160781
|
+
hasControls: false,
|
|
160782
|
+
hasBorders: false,
|
|
160783
|
+
lockMovementX: true,
|
|
160784
|
+
lockMovementY: true
|
|
160785
|
+
});
|
|
160786
|
+
canvas2.add(activeHLine);
|
|
160787
|
+
} else {
|
|
160788
|
+
activeHLine.set({
|
|
160789
|
+
x1,
|
|
160790
|
+
y1: snapLineY,
|
|
160791
|
+
x2,
|
|
160792
|
+
y2: snapLineY,
|
|
160793
|
+
strokeWidth: GUIDELINE_WIDTH / zoom,
|
|
160794
|
+
visible: true
|
|
160795
|
+
});
|
|
160796
|
+
activeHLine.setCoords();
|
|
160797
|
+
}
|
|
160798
|
+
canvas2.bringObjectToFront(activeHLine);
|
|
160799
|
+
} else if (activeHLine) {
|
|
160800
|
+
canvas2.remove(activeHLine);
|
|
160801
|
+
activeHLine = null;
|
|
160802
|
+
}
|
|
160803
|
+
canvas2.requestRenderAll();
|
|
160804
|
+
}
|
|
160323
160805
|
function setupHistoryRecording(canvas2) {
|
|
160324
160806
|
let modifiedTimer = null;
|
|
160325
160807
|
const handleObjectAddedOrRemoved = (e3) => {
|
|
@@ -160482,13 +160964,21 @@ function setupSelectionHandlers(canvas2, setSelection, clearSelection) {
|
|
|
160482
160964
|
configureSelectionControls();
|
|
160483
160965
|
bringOverlaysToFront(canvas2);
|
|
160484
160966
|
};
|
|
160485
|
-
const handleObjectMoving = () => {
|
|
160967
|
+
const handleObjectMoving = (e3) => {
|
|
160968
|
+
if (e3.target) {
|
|
160969
|
+
handleObjectAlignmentSnap(canvas2, e3.target);
|
|
160970
|
+
}
|
|
160486
160971
|
bringOverlaysToFront(canvas2);
|
|
160487
160972
|
};
|
|
160973
|
+
const handleMovementEnd = (e3) => {
|
|
160974
|
+
clearAlignmentGuidelines(canvas2);
|
|
160975
|
+
hideDegree(e3);
|
|
160976
|
+
};
|
|
160488
160977
|
canvas2.on("object:moving", handleObjectMoving);
|
|
160489
|
-
canvas2.on("object:modified",
|
|
160490
|
-
canvas2.on("mouse:up",
|
|
160978
|
+
canvas2.on("object:modified", handleMovementEnd);
|
|
160979
|
+
canvas2.on("mouse:up", handleMovementEnd);
|
|
160491
160980
|
canvas2.on("selection:cleared", () => {
|
|
160981
|
+
clearAlignmentGuidelines(canvas2);
|
|
160492
160982
|
const bgImg = canvas2.getObjects().find((o2) => o2.name === "bg-image");
|
|
160493
160983
|
if (bgImg) {
|
|
160494
160984
|
bgImg.set({
|
|
@@ -160506,13 +160996,14 @@ function setupSelectionHandlers(canvas2, setSelection, clearSelection) {
|
|
|
160506
160996
|
clearSelection();
|
|
160507
160997
|
});
|
|
160508
160998
|
return () => {
|
|
160999
|
+
clearAlignmentGuidelines(canvas2);
|
|
160509
161000
|
canvas2.off("selection:created");
|
|
160510
161001
|
canvas2.off("selection:updated");
|
|
160511
161002
|
canvas2.off("object:scaling", configureSelectionControls);
|
|
160512
161003
|
canvas2.off("object:rotating");
|
|
160513
161004
|
canvas2.off("object:moving", handleObjectMoving);
|
|
160514
|
-
canvas2.off("object:modified",
|
|
160515
|
-
canvas2.off("mouse:up",
|
|
161005
|
+
canvas2.off("object:modified", handleMovementEnd);
|
|
161006
|
+
canvas2.off("mouse:up", handleMovementEnd);
|
|
160516
161007
|
canvas2.off("selection:cleared");
|
|
160517
161008
|
};
|
|
160518
161009
|
}
|
|
@@ -160569,6 +161060,9 @@ const CUSTOM_PROPERTIES = [
|
|
|
160569
161060
|
"lockMovementY",
|
|
160570
161061
|
"rx",
|
|
160571
161062
|
"ry",
|
|
161063
|
+
"src",
|
|
161064
|
+
"crossOrigin",
|
|
161065
|
+
"__maskPath",
|
|
160572
161066
|
"__radiusOfCanvas",
|
|
160573
161067
|
"__shapeType",
|
|
160574
161068
|
"__pageSide",
|
|
@@ -160579,10 +161073,28 @@ const CUSTOM_PROPERTIES = [
|
|
|
160579
161073
|
"__initialZoom",
|
|
160580
161074
|
"__borderRect",
|
|
160581
161075
|
"__rotationControlSize",
|
|
160582
|
-
"__showDegree"
|
|
161076
|
+
"__showDegree",
|
|
161077
|
+
"textAlign",
|
|
161078
|
+
"fontFamily",
|
|
161079
|
+
"fontSize",
|
|
161080
|
+
"fontWeight",
|
|
161081
|
+
"fontStyle",
|
|
161082
|
+
"underline",
|
|
161083
|
+
"linethrough",
|
|
161084
|
+
"overline",
|
|
161085
|
+
"fill",
|
|
161086
|
+
"stroke",
|
|
161087
|
+
"strokeWidth",
|
|
161088
|
+
"lineHeight",
|
|
161089
|
+
"charSpacing",
|
|
161090
|
+
"styles",
|
|
161091
|
+
"paintFirst",
|
|
161092
|
+
"strokeUniform",
|
|
161093
|
+
"objectCaching"
|
|
160583
161094
|
];
|
|
160584
161095
|
async function switchCanvasPage(canvas2, currentPage, targetPage, pageJSON, clearSelection, containerRef) {
|
|
160585
161096
|
if (targetPage === currentPage) return;
|
|
161097
|
+
clearAlignmentGuidelines(canvas2);
|
|
160586
161098
|
const historyStore = useHistoryStore.getState();
|
|
160587
161099
|
useLoaderStore.getState().startLoading();
|
|
160588
161100
|
try {
|
|
@@ -161015,8 +161527,9 @@ function ZoomControls({ onReset }) {
|
|
|
161015
161527
|
] }) });
|
|
161016
161528
|
}
|
|
161017
161529
|
const OFFSET = 20;
|
|
161530
|
+
let globalClipboard = null;
|
|
161531
|
+
let clipboardSourcePage = null;
|
|
161018
161532
|
function useKeyboardEvents(canvasParam) {
|
|
161019
|
-
const clipboardRef = useRef(null);
|
|
161020
161533
|
const storeCanvas = useEditorStore((s2) => s2.canvas);
|
|
161021
161534
|
const historyCanvas = useHistoryStore((s2) => s2.canvas);
|
|
161022
161535
|
const canvas2 = canvasParam || storeCanvas || historyCanvas;
|
|
@@ -161028,30 +161541,56 @@ function useKeyboardEvents(canvasParam) {
|
|
|
161028
161541
|
if (!activeCanvas) return;
|
|
161029
161542
|
const activeObject = activeCanvas.getActiveObject();
|
|
161030
161543
|
if (!activeObject || activeObject.name === "workarea") return;
|
|
161031
|
-
const cloned = await activeObject.clone();
|
|
161032
|
-
|
|
161544
|
+
const cloned = await activeObject.clone(HISTORY_CUSTOM_PROPERTIES);
|
|
161545
|
+
globalClipboard = cloned;
|
|
161546
|
+
clipboardSourcePage = useHistoryStore.getState().currentPage;
|
|
161033
161547
|
};
|
|
161034
161548
|
const handlePaste = async () => {
|
|
161035
161549
|
const activeCanvas = getActiveCanvas();
|
|
161036
|
-
if (!activeCanvas || !
|
|
161037
|
-
const
|
|
161550
|
+
if (!activeCanvas || !globalClipboard) return;
|
|
161551
|
+
const currentSide = useHistoryStore.getState().currentPage;
|
|
161552
|
+
const isCrossPagePaste = clipboardSourcePage && clipboardSourcePage !== currentSide;
|
|
161553
|
+
const clonedObj = await globalClipboard.clone(HISTORY_CUSTOM_PROPERTIES);
|
|
161038
161554
|
activeCanvas.discardActiveObject();
|
|
161555
|
+
const offset = isCrossPagePaste ? 0 : OFFSET;
|
|
161039
161556
|
clonedObj.set({
|
|
161040
|
-
left: (clonedObj.left ?? 0) +
|
|
161041
|
-
top: (clonedObj.top ?? 0) +
|
|
161042
|
-
evented: true
|
|
161557
|
+
left: (clonedObj.left ?? 0) + offset,
|
|
161558
|
+
top: (clonedObj.top ?? 0) + offset,
|
|
161559
|
+
evented: true,
|
|
161560
|
+
selectable: true
|
|
161043
161561
|
});
|
|
161044
|
-
|
|
161045
|
-
|
|
161046
|
-
|
|
161562
|
+
clonedObj.__pageSide = currentSide;
|
|
161563
|
+
globalClipboard.set({
|
|
161564
|
+
left: (globalClipboard.left ?? 0) + OFFSET,
|
|
161565
|
+
top: (globalClipboard.top ?? 0) + OFFSET
|
|
161047
161566
|
});
|
|
161567
|
+
clipboardSourcePage = currentSide;
|
|
161568
|
+
const ensureTextLoaded = async (obj) => {
|
|
161569
|
+
if (obj.type === "textbox" || obj.type === "text" || obj.type === "i-text") {
|
|
161570
|
+
if (obj.fontFamily) {
|
|
161571
|
+
try {
|
|
161572
|
+
await document.fonts.load(`16px "${obj.fontFamily}"`);
|
|
161573
|
+
} catch {
|
|
161574
|
+
}
|
|
161575
|
+
}
|
|
161576
|
+
obj.initDimensions?.();
|
|
161577
|
+
obj.set("dirty", true);
|
|
161578
|
+
}
|
|
161579
|
+
};
|
|
161048
161580
|
if (clonedObj.type?.toLowerCase() === "activeselection") {
|
|
161049
|
-
|
|
161050
|
-
|
|
161051
|
-
|
|
161052
|
-
|
|
161053
|
-
|
|
161581
|
+
const selection = clonedObj;
|
|
161582
|
+
selection.canvas = activeCanvas;
|
|
161583
|
+
await Promise.all(
|
|
161584
|
+
selection.getObjects().map(async (obj) => {
|
|
161585
|
+
await ensureTextLoaded(obj);
|
|
161586
|
+
obj.__pageSide = currentSide;
|
|
161587
|
+
activeCanvas.add(obj);
|
|
161588
|
+
})
|
|
161589
|
+
);
|
|
161590
|
+
selection.setCoords();
|
|
161054
161591
|
} else {
|
|
161592
|
+
await ensureTextLoaded(clonedObj);
|
|
161593
|
+
clonedObj.setCoords();
|
|
161055
161594
|
activeCanvas.add(clonedObj);
|
|
161056
161595
|
}
|
|
161057
161596
|
activeCanvas.setActiveObject(clonedObj);
|
|
@@ -161069,6 +161608,7 @@ function useKeyboardEvents(canvasParam) {
|
|
|
161069
161608
|
const isY = e3.key?.toLowerCase() === "y" || e3.code === "KeyY";
|
|
161070
161609
|
const isC = e3.key?.toLowerCase() === "c" || e3.code === "KeyC";
|
|
161071
161610
|
const isV = e3.key?.toLowerCase() === "v" || e3.code === "KeyV";
|
|
161611
|
+
const isA = e3.key?.toLowerCase() === "a" || e3.code === "KeyA";
|
|
161072
161612
|
if (isZ && !e3.shiftKey) {
|
|
161073
161613
|
e3.preventDefault();
|
|
161074
161614
|
useHistoryStore.getState().undo();
|
|
@@ -161089,6 +161629,22 @@ function useKeyboardEvents(canvasParam) {
|
|
|
161089
161629
|
handlePaste();
|
|
161090
161630
|
return;
|
|
161091
161631
|
}
|
|
161632
|
+
if (isA) {
|
|
161633
|
+
const activeCanvas = getActiveCanvas();
|
|
161634
|
+
if (activeCanvas) {
|
|
161635
|
+
e3.preventDefault();
|
|
161636
|
+
const selectableObjects = activeCanvas.getObjects().filter(
|
|
161637
|
+
(obj) => obj.name !== "workarea" && !obj.name?.startsWith("overlay-") && !obj.name?.startsWith("guideline-") && obj.selectable !== false
|
|
161638
|
+
);
|
|
161639
|
+
if (selectableObjects.length > 0) {
|
|
161640
|
+
activeCanvas.discardActiveObject();
|
|
161641
|
+
const sel = new Qo(selectableObjects, { canvas: activeCanvas });
|
|
161642
|
+
activeCanvas.setActiveObject(sel);
|
|
161643
|
+
activeCanvas.requestRenderAll();
|
|
161644
|
+
}
|
|
161645
|
+
}
|
|
161646
|
+
return;
|
|
161647
|
+
}
|
|
161092
161648
|
const isQuote = e3.key === "'" || e3.code === "Quote";
|
|
161093
161649
|
if (isQuote) {
|
|
161094
161650
|
e3.preventDefault();
|
|
@@ -161118,13 +161674,14 @@ function Workspace() {
|
|
|
161118
161674
|
const drawTool = useDrawStore((s2) => s2.drawTool);
|
|
161119
161675
|
const setDrawTool = useDrawStore((s2) => s2.setDrawTool);
|
|
161120
161676
|
const productData = useProductStore((s2) => s2.productData);
|
|
161121
|
-
const
|
|
161122
|
-
const [currentPage, setCurrentPage] = useState("front");
|
|
161123
|
-
const [isPanning, setIsPanning] = useState(false);
|
|
161677
|
+
const editorPages = useEditorStore((s2) => s2.pages);
|
|
161124
161678
|
const pageJSON = useRef({
|
|
161125
161679
|
front: null,
|
|
161126
161680
|
back: null
|
|
161127
161681
|
});
|
|
161682
|
+
const isTwoSided = productData?.optionsMap?.printing === "4/4" || !!pageJSON.current?.back || !!editorPages?.back;
|
|
161683
|
+
const [currentPage, setCurrentPage] = useState("front");
|
|
161684
|
+
const [isPanning, setIsPanning] = useState(false);
|
|
161128
161685
|
useEffect(() => {
|
|
161129
161686
|
if (!canvasRef.current || !containerRef.current) return;
|
|
161130
161687
|
if (fabricRef.current) return;
|
|
@@ -161302,9 +161859,48 @@ function Workspace() {
|
|
|
161302
161859
|
setCurrentPage(page);
|
|
161303
161860
|
renderGrid(fabricRef.current);
|
|
161304
161861
|
};
|
|
161862
|
+
const copyFrontToBack = async () => {
|
|
161863
|
+
if (!fabricRef.current) return;
|
|
161864
|
+
useLoaderStore.getState().startLoading();
|
|
161865
|
+
try {
|
|
161866
|
+
if (currentPage === "front") {
|
|
161867
|
+
pageJSON.current.front = fabricRef.current.toObject(CUSTOM_PROPERTIES);
|
|
161868
|
+
}
|
|
161869
|
+
let frontData = pageJSON.current.front || useEditorStore.getState().pages?.front;
|
|
161870
|
+
if (!frontData) {
|
|
161871
|
+
console.warn("No front design found to copy");
|
|
161872
|
+
return;
|
|
161873
|
+
}
|
|
161874
|
+
const clonedFrontJson = JSON.parse(JSON.stringify(frontData));
|
|
161875
|
+
await loadTemplateFontsIntoHead(null, clonedFrontJson);
|
|
161876
|
+
pageJSON.current.back = clonedFrontJson;
|
|
161877
|
+
useEditorStore.getState().setPages({
|
|
161878
|
+
...useEditorStore.getState().pages,
|
|
161879
|
+
...pageJSON.current,
|
|
161880
|
+
back: clonedFrontJson
|
|
161881
|
+
});
|
|
161882
|
+
clearSelection();
|
|
161883
|
+
useHistoryStore.getState().setPage("back");
|
|
161884
|
+
setCurrentPage("back");
|
|
161885
|
+
await loadPageToCanvas(fabricRef.current, clonedFrontJson, "back");
|
|
161886
|
+
const workarea = fabricRef.current.getObjects().find((obj) => obj.name === "workarea");
|
|
161887
|
+
if (workarea) {
|
|
161888
|
+
workarea.__pageSide = "back";
|
|
161889
|
+
createOverlays(fabricRef.current, workarea, "back");
|
|
161890
|
+
bringOverlaysToFront(fabricRef.current);
|
|
161891
|
+
}
|
|
161892
|
+
fabricRef.current.requestRenderAll();
|
|
161893
|
+
renderGrid(fabricRef.current);
|
|
161894
|
+
useHistoryStore.getState().saveState();
|
|
161895
|
+
} catch (err) {
|
|
161896
|
+
console.error("Failed to copy front design to back:", err);
|
|
161897
|
+
} finally {
|
|
161898
|
+
useLoaderStore.getState().stopLoading();
|
|
161899
|
+
}
|
|
161900
|
+
};
|
|
161305
161901
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
161306
161902
|
/* @__PURE__ */ jsxs("div", { className: "absolute bottom-6 z-50 flex items-center bg-white/80 backdrop-blur-xl rounded-2xl shadow-[0_8px_32px_rgba(0,0,0,0.12)] px-1.5 py-1.5 border border-white/40 ring-1 ring-black/5", children: [
|
|
161307
|
-
/* @__PURE__ */ jsxs("div", { className: "flex bg-gray-200/50 p-1 rounded-xl", children: [
|
|
161903
|
+
/* @__PURE__ */ jsxs("div", { className: "flex bg-gray-200/50 p-1 rounded-xl items-center gap-1", children: [
|
|
161308
161904
|
/* @__PURE__ */ jsx(
|
|
161309
161905
|
"button",
|
|
161310
161906
|
{
|
|
@@ -161320,6 +161916,37 @@ function Workspace() {
|
|
|
161320
161916
|
className: `px-4 py-2 rounded-lg text-[11px] font-bold tracking-wider transition-all duration-200 cursor-pointer ${currentPage === "back" ? "bg-white text-[#02bc71] shadow-sm" : "text-gray-500 hover:text-gray-700"}`,
|
|
161321
161917
|
children: "BACK"
|
|
161322
161918
|
}
|
|
161919
|
+
),
|
|
161920
|
+
isTwoSided && /* @__PURE__ */ jsxs(
|
|
161921
|
+
"button",
|
|
161922
|
+
{
|
|
161923
|
+
id: "copy-front-to-back-btn",
|
|
161924
|
+
type: "button",
|
|
161925
|
+
onClick: copyFrontToBack,
|
|
161926
|
+
title: "Copy front design to back design",
|
|
161927
|
+
className: "flex items-center gap-1.5 px-3 py-1.5 ml-1 rounded-lg text-[10px] font-bold tracking-wider bg-white/90 hover:bg-white text-gray-700 hover:text-[#02bc71] border border-gray-300/60 shadow-xs transition-all duration-150 cursor-pointer active:scale-95",
|
|
161928
|
+
children: [
|
|
161929
|
+
/* @__PURE__ */ jsxs(
|
|
161930
|
+
"svg",
|
|
161931
|
+
{
|
|
161932
|
+
width: "13",
|
|
161933
|
+
height: "13",
|
|
161934
|
+
viewBox: "0 0 24 24",
|
|
161935
|
+
fill: "none",
|
|
161936
|
+
stroke: "currentColor",
|
|
161937
|
+
strokeWidth: "2.2",
|
|
161938
|
+
strokeLinecap: "round",
|
|
161939
|
+
strokeLinejoin: "round",
|
|
161940
|
+
className: "text-[#02bc71]",
|
|
161941
|
+
children: [
|
|
161942
|
+
/* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
|
|
161943
|
+
/* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
|
|
161944
|
+
]
|
|
161945
|
+
}
|
|
161946
|
+
),
|
|
161947
|
+
/* @__PURE__ */ jsx("span", { children: "COPY FRONT TO BACK" })
|
|
161948
|
+
]
|
|
161949
|
+
}
|
|
161323
161950
|
)
|
|
161324
161951
|
] }),
|
|
161325
161952
|
/* @__PURE__ */ jsx("div", { className: "w-[1px] h-6 bg-gray-300/50 mx-3" }),
|