figmatic-print-builder/useEditorStore.js

369 lines
11 KiB
JavaScript
Raw Permalink Normal View History

2026-06-16 12:58:18 +05:30
import { create } from "zustand";
import { devtools } from "zustand/middleware";
import { v4 as uuidv4 } from "uuid";
const createElementByType = (type, count) => {
const baseId = uuidv4();
if (type === "text") {
return {
id: baseId,
type: "text",
x: 50 + count * 20,
y: 50 + count * 20,
text: "New Text",
fontSize: 18,
fontFamily: "Arial",
fontWeight: "normal",
color: "#000000",
align: "left",
bold: false,
italic: false,
underline: false,
rotation: 0,
};
}
if (type === "image") {
return {
id: baseId,
type: "image",
x: 50 + count * 20,
y: 50 + count * 20,
width: 100,
height: 100,
src: null,
rotation: 0,
};
}
if (type === "qrcode") {
return {
id: baseId,
type: "qrcode",
x: 50 + count * 20,
y: 50 + count * 20,
size: 100,
data: "https://example.com",
rotation: 0,
};
}
if (type === "line") {
return {
id: baseId,
type: "line",
x: 50,
y: 100 + count * 20,
width: 400,
thickness: 2,
color: "#000000",
rotation: 0,
};
}
if (type === "rectangle") {
return {
id: baseId,
type: "rectangle",
x: 50 + count * 20,
y: 50 + count * 20,
width: 200,
height: 100,
fill: "transparent",
stroke: "#000000",
strokeWidth: 2,
cornerRadius: 0,
rotation: 0,
};
}
if (type === "summary") {
return {
id: baseId,
type: "summary",
x: 50,
y: 300 + count * 20,
fields: [
{ key: "items", label: "Items", visible: true, width: 150 },
{ key: "qty", label: "Qty", visible: true, width: 100 },
{ key: "amount", label: "Amount", visible: true, width: 150 },
],
fontSize: 14,
labelColor: "#000000",
valueColor: "#000000",
spacing: 10,
rotation: 0,
};
}
if (type === "table") {
return {
id: baseId,
type: "table",
x: 50,
y: 200 + count * 20,
width: 480,
columns: [
{ key: "sno", label: "S.No", width: 50, visible: true },
{ key: "item", label: "Item", width: 150, visible: true },
{ key: "hsn", label: "HSN", width: 60, visible: true },
{ key: "qty", label: "Qty", width: 50, visible: true },
{ key: "mrp", label: "MRP", width: 60, visible: true },
{ key: "rate", label: "Rate", width: 60, visible: true },
{ key: "dis", label: "Dis", width: 50, visible: true },
{ key: "tax", label: "Tax %", width: 60, visible: true },
{ key: "amt", label: "Amt", width: 70, visible: true },
],
sampleData: [
{ sno: "1", item: "ITEM 1", hsn: "01", qty: "2", mrp: "100", rate: "88", dis: "0", tax: "5", amt: "176" },
{ sno: "2", item: "ITEM 2", hsn: "02", qty: "1", mrp: "350", rate: "300", dis: "0", tax: "5", amt: "300" },
{ sno: "3", item: "ITEM 3", hsn: "03", qty: "1", mrp: "250", rate: "200", dis: "0", tax: "0", amt: "200" },
{ sno: "4", item: "ITEM 4", hsn: "04", qty: "1", mrp: "50", rate: "30", dis: "0", tax: "18", amt: "30" },
{ sno: "5", item: "ITEM 5", hsn: "05", qty: "1", mrp: "50", rate: "30", dis: "0", tax: "40", amt: "30" },
],
rowHeight: 30,
headerBg: "#f0f0f0",
borderColor: "#000000",
showSampleData: true,
rotation: 0,
};
}
return null;
};
const useEditorStore = create(
devtools((set, get) => ({
// State
elements: [],
selectedId: null,
clipboard: null,
history: [],
historyIndex: -1,
page: {
width: 794,
height: 1123,
name: "A4",
},
showLayers: true,
// Actions
addElement: (type) => {
const state = get();
const newElement = createElementByType(type, state.elements.length);
if (!newElement) return;
set((state) => ({
elements: [...state.elements, newElement],
selectedId: newElement.id,
history: [...state.history.slice(0, state.historyIndex + 1), state.elements],
historyIndex: state.historyIndex + 1,
}));
},
updateElement: (id, updates) => {
set((state) => ({
elements: state.elements.map((el) =>
el.id === id ? { ...el, ...updates } : el
),
}));
},
deleteElement: (id) => {
set((state) => ({
elements: state.elements.filter((el) => el.id !== id),
selectedId: state.selectedId === id ? null : state.selectedId,
}));
},
deleteSelected: () => {
const { selectedId, deleteElement } = get();
if (selectedId) deleteElement(selectedId);
},
setSelectedId: (id) => set({ selectedId: id }),
duplicateElement: (id) => {
const state = get();
const element = state.elements.find((el) => el.id === id);
if (!element) return;
const newElement = {
...element,
id: uuidv4(),
x: element.x + 20,
y: element.y + 20,
};
set((state) => ({
elements: [...state.elements, newElement],
selectedId: newElement.id,
}));
},
copyElement: () => {
const { selectedId, elements } = get();
const element = elements.find((el) => el.id === selectedId);
if (element) {
set({ clipboard: element });
}
},
pasteElement: () => {
const { clipboard } = get();
if (!clipboard) return;
const newElement = {
...clipboard,
id: uuidv4(),
x: clipboard.x + 20,
y: clipboard.y + 20,
};
set((state) => ({
elements: [...state.elements, newElement],
selectedId: newElement.id,
}));
},
moveElement: (id, direction) => {
const step = 10;
const deltas = {
up: { x: 0, y: -step },
down: { x: 0, y: step },
left: { x: -step, y: 0 },
right: { x: step, y: 0 },
};
const delta = deltas[direction];
if (!delta) return;
set((state) => ({
elements: state.elements.map((el) =>
el.id === id
? { ...el, x: el.x + delta.x, y: el.y + delta.y }
: el
),
}));
},
reorderColumns: (elementId, newColumns) => {
set((state) => ({
elements: state.elements.map((el) =>
el.id === elementId ? { ...el, columns: newColumns } : el
),
}));
},
toggleColumnVisibility: (elementId, columnKey) => {
set((state) => ({
elements: state.elements.map((el) => {
if (el.id === elementId && el.type === "table") {
return {
...el,
columns: el.columns.map((col) =>
col.key === columnKey
? { ...col, visible: !col.visible }
: col
),
};
}
return el;
}),
}));
},
updateColumnWidth: (elementId, columnKey, width) => {
set((state) => ({
elements: state.elements.map((el) => {
if (el.id === elementId && el.type === "table") {
return {
...el,
columns: el.columns.map((col) =>
col.key === columnKey ? { ...col, width } : col
),
};
}
return el;
}),
}));
},
updateColumnLabel: (elementId, columnKey, label) => {
set((state) => ({
elements: state.elements.map((el) => {
if (el.id === elementId && el.type === "table") {
return {
...el,
columns: el.columns.map((col) =>
col.key === columnKey ? { ...col, label } : col
),
};
}
return el;
}),
}));
},
// Undo/Redo
undo: () => {
const { historyIndex, history } = get();
if (historyIndex > 0) {
set({
elements: history[historyIndex - 1],
historyIndex: historyIndex - 1,
});
}
},
redo: () => {
const { historyIndex, history } = get();
if (historyIndex < history.length - 1) {
set({
elements: history[historyIndex + 1],
historyIndex: historyIndex + 1,
});
}
},
// Page settings
setPageSize: (size) => {
set({ page: { ...get().page, ...size } });
},
toggleLayers: () => {
set((state) => ({ showLayers: !state.showLayers }));
},
// Save/Load
exportTemplate: () => {
const { elements, page } = get();
return JSON.stringify({ elements, page }, null, 2);
},
importTemplate: (jsonString) => {
try {
const data = JSON.parse(jsonString);
set({
elements: data.elements || [],
page: data.page || get().page,
selectedId: null,
});
} catch (error) {
console.error("Invalid template JSON", error);
}
},
clearCanvas: () => {
set({
elements: [],
selectedId: null,
history: [],
historyIndex: -1,
});
},
}))
);
export default useEditorStore;