From 43ab1424c204a72f427835e2b8e89773d1ab5295 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Thu, 16 May 2024 14:08:59 +0530 Subject: [PATCH 01/15] fix: remove direct comparison of elements array we need to remove specific comparison of elements array in the code as now we are planning to create multi page structure. --- .../components/layout/AppCanvas.vue | 10 ++++---- .../composables/AttachKeyBindings.js | 4 ++-- .../print_designer/composables/Draggable.js | 4 ++-- .../js/print_designer/composables/DropZone.js | 6 ++--- .../js/print_designer/composables/Element.js | 4 ++-- .../print_designer/composables/Resizable.js | 4 ++-- .../js/print_designer/defaultObjects.js | 24 +++++-------------- .../js/print_designer/store/ElementStore.js | 6 ++--- .../public/js/print_designer/utils.js | 14 +++++------ 9 files changed, 32 insertions(+), 44 deletions(-) diff --git a/print_designer/public/js/print_designer/components/layout/AppCanvas.vue b/print_designer/public/js/print_designer/components/layout/AppCanvas.vue index bbc0a54..8052de6 100644 --- a/print_designer/public/js/print_designer/components/layout/AppCanvas.vue +++ b/print_designer/public/js/print_designer/components/layout/AppCanvas.vue @@ -267,7 +267,7 @@ const handleMouseUp = (e) => { } if (MainStore.isDrawing && MainStore.lastCreatedElement?.type == "rectangle") { const recursiveParentLoop = (currentElement, offset = { startX: 0, startY: 0 }) => { - if (currentElement.parent != ElementStore.Elements) { + if (!Array.isArray(currentElement.parent)) { let currentDOM = MainStore.lastCreatedElement.DOMRef.getBoundingClientRect(); let parentDOM = currentElement.parent.DOMRef.getBoundingClientRect(); if ( @@ -296,7 +296,7 @@ const handleMouseUp = (e) => { MainStore.currentElements[tempElement.id] = tempElement; return; } - } else if (MainStore.lastCreatedElement.parent != ElementStore.Elements) { + } else if (!Array.isArray(MainStore.lastCreatedElement.parent)) { let tempElement = { ...MainStore.lastCreatedElement.parent.childrens.pop() }; tempElement.id = frappe.utils.get_random(10); tempElement.index = null; @@ -321,7 +321,7 @@ const handleMouseUp = (e) => { right: MainStore.lastCreatedElement.startX + MainStore.lastCreatedElement.width, }; let parentElement; - if (MainStore.lastCreatedElement.parent == ElementStore.Elements) { + if (Array.isArray(MainStore.lastCreatedElement.parent)) { parentElement = MainStore.lastCreatedElement.parent; } else { parentElement = MainStore.lastCreatedElement.parent.childrens; @@ -342,7 +342,7 @@ const handleMouseUp = (e) => { Rect.bottom > elementRect.bottom ) { let splicedElement; - if (element.parent == ElementStore.Elements) { + if (Array.isArray(element.parent)) { splicedElement = { ...element.parent.splice(element.parent.indexOf(element), 1)[0], }; @@ -360,7 +360,7 @@ const handleMouseUp = (e) => { element.startY - MainStore.lastCreatedElement.startY; splicedElement.parent = MainStore.lastCreatedElement; recursiveChildrens({ element: splicedElement, isClone: false }); - if (splicedElement.parent === ElementStore.Elements) { + if (Array.isArray(splicedElement.parent)) { splicedElement.parent.push(splicedElement); } else { splicedElement.parent.childrens.push(splicedElement); diff --git a/print_designer/public/js/print_designer/composables/AttachKeyBindings.js b/print_designer/public/js/print_designer/composables/AttachKeyBindings.js index 1449d43..3ebf97b 100644 --- a/print_designer/public/js/print_designer/composables/AttachKeyBindings.js +++ b/print_designer/public/js/print_designer/composables/AttachKeyBindings.js @@ -9,7 +9,7 @@ export function useAttachKeyBindings() { function updateStartXY(axis, value) { MainStore.getCurrentElementsValues.forEach((element) => { let restrict; - if (element.parent === ElementStore.Elements) { + if (Array.isArray(element.parent)) { restrict = MainStore.mainContainer.getBoundingClientRect(); } else { restrict = element.parent.DOMRef.getBoundingClientRect(); @@ -33,7 +33,7 @@ export function useAttachKeyBindings() { function updateWidthHeight(key, value) { MainStore.getCurrentElementsValues.forEach((element) => { let restrict; - if (element.parent === ElementStore.Elements) { + if (Array.isArray(element.parent)) { restrict = MainStore.mainContainer.getBoundingClientRect(); } else { restrict = element.parent.DOMRef.getBoundingClientRect(); diff --git a/print_designer/public/js/print_designer/composables/Draggable.js b/print_designer/public/js/print_designer/composables/Draggable.js index 05fb6bc..dc91858 100644 --- a/print_designer/public/js/print_designer/composables/Draggable.js +++ b/print_designer/public/js/print_designer/composables/Draggable.js @@ -59,14 +59,14 @@ export function useDraggable({ } if ( !e.dropzone && - e.target.piniaElementRef.parent !== ElementStore.Elements && + !Array.isArray(e.target.piniaElementRef.parent) && !MainStore.lastCloned ) { let splicedElement; let currentRect = e.target.getBoundingClientRect(); let canvasRect = MainStore.mainContainer.getBoundingClientRect(); let currentParent = e.target.piniaElementRef.parent; - if (currentParent == ElementStore.Elements) { + if (Array.isArray(currentParent)) { splicedElement = currentParent.splice(e.target.piniaElementRef.index, 1)[0]; } else { splicedElement = currentParent.childrens.splice( diff --git a/print_designer/public/js/print_designer/composables/DropZone.js b/print_designer/public/js/print_designer/composables/DropZone.js index b834653..6846a9a 100644 --- a/print_designer/public/js/print_designer/composables/DropZone.js +++ b/print_designer/public/js/print_designer/composables/DropZone.js @@ -21,7 +21,7 @@ export function useDropZone({ element }) { let dropRect = event.dropzone.target.getBoundingClientRect(); if (currentDROP === currentRef.parent) return; let splicedElement; - if (currentRef.parent == ElementStore.Elements) { + if (Array.isArray(currentRef.parent)) { splicedElement = currentRef.parent.splice(currentRef.index, 1)[0]; } else { splicedElement = currentRef.parent.childrens.splice(currentRef.index, 1)[0]; @@ -31,8 +31,8 @@ export function useDropZone({ element }) { splicedElement.startY = currentRect.top - dropRect.top; splicedElement.parent = currentDROP; recursiveChildrens({ element: splicedElement, isClone: false }); - if (currentDROP === ElementStore.Elements) { - ElementStore.Elements.push(splicedElement); + if (Array.isArray(currentDROP)) { + currentDROP.push(splicedElement); } else { currentDROP.childrens.push(splicedElement); } diff --git a/print_designer/public/js/print_designer/composables/Element.js b/print_designer/public/js/print_designer/composables/Element.js index 57d7341..3ff1485 100644 --- a/print_designer/public/js/print_designer/composables/Element.js +++ b/print_designer/public/js/print_designer/composables/Element.js @@ -128,7 +128,7 @@ export function useElement({ draggable = true, resizable = true }) { }, dragStartListener: (e) => { let parentRect; - if (element.parent == ElementStore.Elements) { + if (Array.isArray(element.parent)) { parentRect = MainStore.mainContainer.getBoundingClientRect(); } else { parentRect = element.parent.DOMRef.getBoundingClientRect(); @@ -187,7 +187,7 @@ export function useElement({ draggable = true, resizable = true }) { restrict: MainStore.mainContainer, resizeStartListener: (e) => { let parentRect; - if (element.parent == ElementStore.Elements) { + if (Array.isArray(element.parent)) { parentRect = MainStore.mainContainer.getBoundingClientRect(); } else { parentRect = element.parent.DOMRef.getBoundingClientRect(); diff --git a/print_designer/public/js/print_designer/composables/Resizable.js b/print_designer/public/js/print_designer/composables/Resizable.js index 91e3f01..99af2f9 100644 --- a/print_designer/public/js/print_designer/composables/Resizable.js +++ b/print_designer/public/js/print_designer/composables/Resizable.js @@ -48,14 +48,14 @@ export function useResizable({ if (element.parent == e.target.piniaElementRef.parent) return; if ( !e.dropzone && - e.target.piniaElementRef.parent !== ElementStore.Elements && + !Array.isArray(e.target.piniaElementRef.parent) && !MainStore.lastCloned ) { let splicedElement; let currentRect = e.target.getBoundingClientRect(); let canvasRect = MainStore.mainContainer.getBoundingClientRect(); let currentParent = e.target.piniaElementRef.parent; - if (currentParent == ElementStore.Elements) { + if (Array.isArray(currentParent)) { splicedElement = currentParent.splice( e.target.piniaElementRef.index, 1 diff --git a/print_designer/public/js/print_designer/defaultObjects.js b/print_designer/public/js/print_designer/defaultObjects.js index 66f0680..74dafd1 100644 --- a/print_designer/public/js/print_designer/defaultObjects.js +++ b/print_designer/public/js/print_designer/defaultObjects.js @@ -35,9 +35,7 @@ export const createRectangle = (cordinates, parent = null) => { classes: [], }; - parent !== ElementStore.Elements - ? parent.childrens.push(newRectangle) - : ElementStore.Elements.push(newRectangle); + Array.isArray(parent) ? parent.push(newRectangle) : parent.childrens.push(newRectangle); MainStore.lastCreatedElement = newRectangle; return newRectangle; }; @@ -76,9 +74,7 @@ export const createImage = (cordinates, parent = null) => { classes: [], }; - parent !== ElementStore.Elements - ? parent.childrens.push(newImage) - : ElementStore.Elements.push(newImage); + Array.isArray(parent) ? parent.push(newImage) : parent.childrens.push(newImage); MainStore.lastCreatedElement = newImage; return newImage; }; @@ -121,9 +117,7 @@ export const createBarcode = (cordinates, parent = null) => { classes: [], }; - parent !== ElementStore.Elements - ? parent.childrens.push(newBarcode) - : ElementStore.Elements.push(newBarcode); + Array.isArray(parent) ? parent.push(newBarcode) : parent.childrens.push(newBarcode); MainStore.lastCreatedElement = newBarcode; return newBarcode; }; @@ -170,9 +164,7 @@ export const createTable = (cordinates, parent = null) => { classes: [], }; - parent !== ElementStore.Elements - ? parent.childrens.push(newTable) - : ElementStore.Elements.push(newTable); + Array.isArray(parent) ? parent.push(newTable) : parent.childrens.push(newTable); MainStore.lastCreatedElement = newTable; return newTable; }; @@ -215,9 +207,7 @@ export const createText = (cordinates, parent = null) => { style: {}, classes: [], }; - parent !== ElementStore.Elements - ? parent.childrens.push(newStaticText) - : ElementStore.Elements.push(newStaticText); + Array.isArray(parent) ? parent.push(newStaticText) : parent.childrens.push(newStaticText); MainStore.lastCreatedElement = newStaticText; return newStaticText; }; @@ -262,9 +252,7 @@ export const createDynamicText = (cordinates, parent = null) => { heightType: "auto", classes: [], }; - parent !== ElementStore.Elements - ? parent.childrens.push(newDynamicText) - : ElementStore.Elements.push(newDynamicText); + Array.isArray(parent) ? parent.push(newDynamicText) : parent.childrens.push(newDynamicText); MainStore.lastCreatedElement = newDynamicText; return newDynamicText; }; diff --git a/print_designer/public/js/print_designer/store/ElementStore.js b/print_designer/public/js/print_designer/store/ElementStore.js index 563ea5e..db4e30e 100644 --- a/print_designer/public/js/print_designer/store/ElementStore.js +++ b/print_designer/public/js/print_designer/store/ElementStore.js @@ -54,9 +54,9 @@ export const useElementStore = defineStore("ElementStore", { footer: [], }; // {childrens: []} is passed because we update parent in createRectangle function. - let headerElements = { childrens: [] }; - let bodyElements = { childrens: [] }; - let footerElements = { childrens: [] }; + let headerElements = []; + let bodyElements = []; + let footerElements = []; // WARNING: 2 lines below are for debugging purpose only. // this.Elements.length = 0; // headerElements = bodyElements = footerElements = this.Elements; diff --git a/print_designer/public/js/print_designer/utils.js b/print_designer/public/js/print_designer/utils.js index b7577eb..bf62e6b 100644 --- a/print_designer/public/js/print_designer/utils.js +++ b/print_designer/public/js/print_designer/utils.js @@ -203,13 +203,13 @@ const childrensCleanUp = (parentElement, element, isClone, isMainElement) => { } } if (isMainElement && isClone) { - if (parentElement.parent === ElementStore.Elements) { + if (Array.isArray(parentElement.parent)) { parentElement.parent.push(element); } else { parentElement.parent.childrens.push(element); } } else if (!isMainElement) { - if (parentElement === ElementStore.Elements) { + if (Array.isArray(parentElement)) { parentElement.push(element); } else { parentElement.childrens.push(element); @@ -319,14 +319,14 @@ export const deleteCurrentElements = () => { if (MainStore.getCurrentElementsValues.length === 1) { let curobj = MainStore.getCurrentElementsValues[0]; deleteDynamicReferance(curobj); - if (curobj.parent == ElementStore.Elements) { + if (Array.isArray(curobj.parent)) { deleteSnapObjects(curobj.parent.splice(curobj.index, 1)[0], true); } else { deleteSnapObjects(curobj.parent.childrens.splice(curobj.index, 1)[0], true); } } else { MainStore.getCurrentElementsValues.forEach((element) => { - if (element.parent == ElementStore.Elements) { + if (Array.isArray(element.parent)) { deleteSnapObjects( element.parent.splice(element.parent.indexOf(element), 1)[0], true @@ -453,7 +453,7 @@ export const handleAlignIconClick = (value) => { let parent; MainStore.getCurrentElementsValues.forEach((element) => { if (parent == null) { - if (element.parent == ElementStore.Elements) { + if (Array.isArray(element.parent)) { parent = false; return; } @@ -812,9 +812,9 @@ export const checkUpdateElementOverlapping = (element = null) => { const MainStore = useMainStore(); const ElementStore = useElementStore(); nextTick(() => { - if (element && element.parent != ElementStore.Elements) return; + if (element && !Array.isArray(element.parent)) return; isOlderSchema = MainStore.isOlderSchema("1.1.0"); - ElementStore.Elements.forEach((el) => { + element.parent.forEach((el) => { const isElementOverlapping = ElementStore.isElementOverlapping(el); if (el.isElementOverlapping != isElementOverlapping) { el.isElementOverlapping = isElementOverlapping; From c2121e818fb17b6388fc071188ddd9332ac1524f Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Sun, 2 Jun 2024 13:26:54 +0530 Subject: [PATCH 02/15] feat: Header / Footer Doctype Moved header / footer to separate doctype. --- .../doctype/pd_header_footer/__init__.py | 0 .../pd_header_footer/pd_header_footer.js | 8 ++ .../pd_header_footer/pd_header_footer.json | 81 +++++++++++++++++++ .../pd_header_footer/pd_header_footer.py | 9 +++ .../pd_header_footer/test_pd_header_footer.py | 9 +++ 5 files changed, 107 insertions(+) create mode 100644 print_designer/print_designer/doctype/pd_header_footer/__init__.py create mode 100644 print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js create mode 100644 print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json create mode 100644 print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.py create mode 100644 print_designer/print_designer/doctype/pd_header_footer/test_pd_header_footer.py diff --git a/print_designer/print_designer/doctype/pd_header_footer/__init__.py b/print_designer/print_designer/doctype/pd_header_footer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js new file mode 100644 index 0000000..3d8ec21 --- /dev/null +++ b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, Frappe Technologies Pvt Ltd. and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("PD Header Footer", { +// refresh(frm) { + +// }, +// }); diff --git a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json new file mode 100644 index 0000000..3fb387a --- /dev/null +++ b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json @@ -0,0 +1,81 @@ +{ + "actions": [], + "autoname": "field:title", + "creation": "2024-06-02 13:05:19.762196", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "section_break_otix", + "title", + "column_break_lcet", + "print_format", + "section_break_codz", + "header", + "footer" + ], + "fields": [ + { + "fieldname": "header", + "fieldtype": "JSON", + "label": "Header" + }, + { + "fieldname": "footer", + "fieldtype": "JSON", + "label": "Footer" + }, + { + "fieldname": "section_break_otix", + "fieldtype": "Section Break", + "options": "Print Format" + }, + { + "fieldname": "column_break_lcet", + "fieldtype": "Column Break" + }, + { + "fieldname": "print_format", + "fieldtype": "Link", + "label": "Print Format", + "link_filters": "[[\"Print Format\",\"print_designer\",\"=\",1]]", + "options": "Print Format", + "read_only": 1 + }, + { + "fieldname": "section_break_codz", + "fieldtype": "Section Break" + }, + { + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "length": 100, + "unique": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-06-02 13:16:05.140853", + "modified_by": "Administrator", + "module": "Print Designer", + "name": "PD Header Footer", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.py b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.py new file mode 100644 index 0000000..0b588aa --- /dev/null +++ b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Frappe Technologies Pvt Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PDHeaderFooter(Document): + pass diff --git a/print_designer/print_designer/doctype/pd_header_footer/test_pd_header_footer.py b/print_designer/print_designer/doctype/pd_header_footer/test_pd_header_footer.py new file mode 100644 index 0000000..ae3bf3c --- /dev/null +++ b/print_designer/print_designer/doctype/pd_header_footer/test_pd_header_footer.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Frappe Technologies Pvt Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPDHeaderFooter(FrappeTestCase): + pass From 1d5caf92f35aaddf8a1ece840ec10a7eb16c7f02 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Sun, 2 Jun 2024 13:43:54 +0530 Subject: [PATCH 03/15] chore: better structure for header/footer doctype --- .../print_designer/doctype/__init__.py | 0 .../pd_header_footer/pd_header_footer.js | 17 ++++++-- .../pd_header_footer/pd_header_footer.json | 39 ++++++++++++------- 3 files changed, 39 insertions(+), 17 deletions(-) create mode 100644 print_designer/print_designer/doctype/__init__.py diff --git a/print_designer/print_designer/doctype/__init__.py b/print_designer/print_designer/doctype/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js index 3d8ec21..0500ec7 100644 --- a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js +++ b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js @@ -1,8 +1,17 @@ // Copyright (c) 2024, Frappe Technologies Pvt Ltd. and contributors // For license information, please see license.txt -// frappe.ui.form.on("PD Header Footer", { -// refresh(frm) { +const set_template_app_options = (frm) => { + frappe.xcall("frappe.core.doctype.module_def.module_def.get_installed_apps").then((r) => { + frm.set_df_property("export_template_app", "options", JSON.parse(r)); + if (!frm.doc.export_template_app) { + frm.set_value("export_template_app", "print_designer"); + } + }); +}; -// }, -// }); +frappe.ui.form.on("PD Header Footer", { + refresh(frm) { + set_template_app_options(frm); + }, +}); diff --git a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json index 3fb387a..7a1fbcf 100644 --- a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json +++ b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json @@ -7,23 +7,15 @@ "field_order": [ "section_break_otix", "title", + "export_template_app", "column_break_lcet", "print_format", + "type", "section_break_codz", - "header", - "footer" + "data", + "settings" ], "fields": [ - { - "fieldname": "header", - "fieldtype": "JSON", - "label": "Header" - }, - { - "fieldname": "footer", - "fieldtype": "JSON", - "label": "Footer" - }, { "fieldname": "section_break_otix", "fieldtype": "Section Break", @@ -51,11 +43,32 @@ "label": "Title", "length": 100, "unique": 1 + }, + { + "fieldname": "type", + "fieldtype": "Select", + "label": "Type", + "options": "Header\nFooter" + }, + { + "fieldname": "export_template_app", + "fieldtype": "Select", + "label": "Export Template App" + }, + { + "fieldname": "data", + "fieldtype": "JSON", + "label": "Data" + }, + { + "fieldname": "settings", + "fieldtype": "JSON", + "label": "Settings" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-06-02 13:16:05.140853", + "modified": "2024-06-02 13:41:44.173915", "modified_by": "Administrator", "module": "Print Designer", "name": "PD Header Footer", From 353f1d0337cedf098c318d86f71422c44712e2cb Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Thu, 6 Jun 2024 15:14:03 +0530 Subject: [PATCH 04/15] feat: Multi Page and Multi Header/Footer - added patch to convert all json to match new format with pages. - updated jinja formats to loop over pages and render them. - modified code to design multiple headers and footers. - added option to add new pages. - updated drag, resize, drop to work with multiple pages. - updated marquee tool. - added options to select headers / footers for first / last / odd /even pages - Changed Canvas to render multiple pages. - Header / footer now needs to designed separately. --- print_designer/patches.txt | 1 + .../move_header_footers_to_new_schema.py | 76 +++ .../print_designer/jinja/header_footer.html | 65 +- .../jinja/old_print_format.html | 6 +- .../print_designer/jinja/print_format.html | 18 +- .../page/print_designer/print_designer.js | 31 + .../js/print_designer/PropertiesPanelState.js | 147 ++++- .../components/base/BaseDynamicText.vue | 6 +- .../components/base/BaseRectangle.vue | 60 +- .../components/layout/AppCanvas.vue | 603 +++++++----------- .../components/layout/AppPages.vue | 447 +++++++++++++ .../components/layout/AppPdfSetup.vue | 225 ------- .../components/layout/AppPropertiesPanel.vue | 23 +- .../layout/AppPropertiesPanelSection.vue | 19 +- .../composables/AttachKeyBindings.js | 22 +- .../print_designer/composables/Draggable.js | 29 - .../js/print_designer/composables/DropZone.js | 15 +- .../js/print_designer/composables/Element.js | 70 +- .../composables/MarqueeSelectionTool.js | 57 +- .../print_designer/composables/Resizable.js | 25 +- .../js/print_designer/defaultObjects.js | 25 +- .../js/print_designer/icons/IconsUse.vue | 2 +- .../js/print_designer/store/ElementStore.js | 233 ++++--- .../js/print_designer/store/MainStore.js | 33 +- .../print_designer/store/fetchMetaAndData.js | 100 +-- .../public/js/print_designer/utils.js | 180 ++++-- 26 files changed, 1588 insertions(+), 930 deletions(-) create mode 100644 print_designer/patches/move_header_footers_to_new_schema.py create mode 100644 print_designer/public/js/print_designer/components/layout/AppPages.vue delete mode 100644 print_designer/public/js/print_designer/components/layout/AppPdfSetup.vue diff --git a/print_designer/patches.txt b/print_designer/patches.txt index 5cd0b74..3de7206 100644 --- a/print_designer/patches.txt +++ b/print_designer/patches.txt @@ -15,3 +15,4 @@ print_designer.patches.introduce_dynamic_height print_designer.patches.remove_unused_rectangle_gs_properties print_designer.patches.change_dynamic_height_variable print_designer.patches.introduce_z_index +print_designer.patches.move_header_footers_to_new_schema diff --git a/print_designer/patches/move_header_footers_to_new_schema.py b/print_designer/patches/move_header_footers_to_new_schema.py new file mode 100644 index 0000000..0eb9e67 --- /dev/null +++ b/print_designer/patches/move_header_footers_to_new_schema.py @@ -0,0 +1,76 @@ +import frappe + + +def patch_format(): + print_formats = frappe.get_all( + "Print Format", + filters={"print_designer": 1}, + fields=[ + "name", + "print_designer_header", + "print_designer_body", + "print_designer_after_table", + "print_designer_footer", + "print_designer_print_format", + "print_designer_settings", + ], + ) + for pf in print_formats: + settings = frappe.json.loads(pf.print_designer_settings or "{}") + + header_childrens = frappe.json.loads(pf.print_designer_header or "[]") + header_data = [ + { + "type": "page", + "childrens": header_childrens, + "firstPage": True, + "oddPage": True, + "evenPage": True, + "lastPage": True, + } + ] + + footer_childrens = frappe.json.loads(pf.print_designer_footer or "[]") + footer_data = [ + { + "type": "page", + "childrens": footer_childrens, + "firstPage": True, + "oddPage": True, + "evenPage": True, + "lastPage": True, + } + ] + for child in footer_childrens: + child["startY"] -= ( + settings["page"].get("height", 0) + - settings["page"].get("marginTop", 0) + - settings["page"].get("footerHeight", 0) + ) + + childrens = frappe.json.loads(pf.print_designer_body or "[]") + bodyPage = [ + { + "index": 0, + "type": "page", + "childrens": childrens, + "isDropZone": True, + } + ] + + frappe.set_value( + "Print Format", + pf.name, + { + "print_designer_header": frappe.json.dumps(header_data), + "print_designer_body": frappe.json.dumps(bodyPage), + "print_designer_footer": frappe.json.dumps(footer_data), + "print_designer_settings": frappe.json.dumps(settings), + }, + ) + return print_formats + + +def execute(): + """Updating Table and Dynamic Text Elements to have property isDynamicHeight with default value as True""" + patch_format() diff --git a/print_designer/print_designer/page/print_designer/jinja/header_footer.html b/print_designer/print_designer/page/print_designer/jinja/header_footer.html index e7f024d..552e490 100644 --- a/print_designer/print_designer/page/print_designer/jinja/header_footer.html +++ b/print_designer/print_designer/page/print_designer/jinja/header_footer.html @@ -51,7 +51,70 @@ y[j].textContent = vars[x[i]]; } } - } + + const headers = { + firstPage : document.getElementById("firstPageHeader"), + oddPage : document.getElementById("oddPageHeader"), + evenPage : document.getElementById("evenPageHeader"), + lastPage : document.getElementById("lastPageHeader"), + } + const footers = { + firstPage : document.getElementById("firstPageFooter"), + oddPage : document.getElementById("oddPageFooter"), + evenPage : document.getElementById("evenPageFooter"), + lastPage : document.getElementById("lastPageFooter"), + } + function displayHeaderFooter(elements, selectedElements) { + for (var key in elements) { + if (elements[key]) { + if (elements[key] === selectedElements) { + elements[key].style.display = "block"; + } else { + elements[key].style.display = "none"; + } + } + } + } + var page_no = parseInt(vars["page"]); + var total_page_no = parseInt(vars["topage"]); + if (page_no == 1) { + if (headers.firstPage) { + displayHeaderFooter(headers, headers.firstPage); + } else { + displayHeaderFooter(headers, headers.oddPage); + } + if (footers.firstPage) { + displayHeaderFooter(footers, footers.firstPage); + } else { + displayHeaderFooter(footers, footers.oddPage); + } + } else if (page_no == total_page_no) { + if (headers.lastPage) { + displayHeaderFooter(headers, headers.lastPage); + } else { + if (total_page_no % 2 == 0) { + displayHeaderFooter(headers, headers.evenPage); + } else { + displayHeaderFooter(headers, headers.oddPage); + } + } + if (footers.lastPage) { + displayHeaderFooter(footers, footers.lastPage); + } else { + if (total_page_no % 2 == 0) { + displayHeaderFooter(footers, footers.evenPage); + } else { + displayHeaderFooter(footers, footers.oddPage); + } + } + } else if (page_no % 2 == 0) { + displayHeaderFooter(headers, headers.evenPage); + displayHeaderFooter(footers, footers.evenPage); + } else { + displayHeaderFooter(headers, headers.oddPage); + displayHeaderFooter(footers, footers.oddPage); + } + } {% for tag in styles -%} diff --git a/print_designer/print_designer/page/print_designer/jinja/old_print_format.html b/print_designer/print_designer/page/print_designer/jinja/old_print_format.html index 7e0e1cf..85f3f0c 100644 --- a/print_designer/print_designer/page/print_designer/jinja/old_print_format.html +++ b/print_designer/print_designer/page/print_designer/jinja/old_print_format.html @@ -205,9 +205,9 @@ - {% set renderHeader = render_element(headerElement, send_to_jinja) %} - {% set renderBody = render_element(bodyElement, send_to_jinja) %} - {% set renderFooter = render_element(footerElement, send_to_jinja) %} + {% set renderHeader = render_element(headerElement[0].childrens, send_to_jinja) %} + {% set renderBody = render_element(bodyElement[0].childrens, send_to_jinja) %} + {% set renderFooter = render_element(footerElement[0].childrens, send_to_jinja) %} {% if settings.printHeaderFonts %} {% set printHeaderFonts = getFontStyles(settings.printHeaderFonts) %} diff --git a/print_designer/print_designer/page/print_designer/jinja/print_format.html b/print_designer/print_designer/page/print_designer/jinja/print_format.html index 8624edb..988cb7e 100644 --- a/print_designer/print_designer/page/print_designer/jinja/print_format.html +++ b/print_designer/print_designer/page/print_designer/jinja/print_format.html @@ -10,16 +10,26 @@
-
+
- {% if headerElement %}{{ render(pd_format.header, send_to_jinja) }}{%endif%} +
{% if pd_format.header.firstPage %}{{ render(pd_format.header.firstPage, send_to_jinja) }}{%endif%}
+ + + +
- {% if bodyElement %}{{ render(pd_format.body, send_to_jinja) }}{%endif%} + {%- for body in pd_format.body -%} + {{ render(body.childrens, send_to_jinja) }} + {%- endfor -%}
diff --git a/print_designer/print_designer/page/print_designer/print_designer.js b/print_designer/print_designer/page/print_designer/print_designer.js index e4b168c..df965d5 100644 --- a/print_designer/print_designer/page/print_designer/print_designer.js +++ b/print_designer/print_designer/page/print_designer/print_designer.js @@ -76,6 +76,37 @@ const printDesignerDialog = () => { name: print_format_name, doc_type: doctype, print_designer: 1, + print_designer_header: JSON.stringify([ + { + type: "page", + childrens: [], + firstPage: true, + oddPage: true, + evenPage: true, + lastPage: true, + DOMRef: null, + }, + ]), + print_designer_body: JSON.stringify([ + { + type: "page", + index: 0, + DOMRef: null, + isDropZone: true, + childrens: [], + }, + ]), + print_designer_footer: JSON.stringify([ + { + type: "page", + childrens: [], + firstPage: true, + oddPage: true, + evenPage: true, + lastPage: true, + DOMRef: null, + }, + ]), }) .then((doc) => { // Incase Route is Same, set_route() is needed to refresh. diff --git a/print_designer/public/js/print_designer/PropertiesPanelState.js b/print_designer/public/js/print_designer/PropertiesPanelState.js index e82fca5..429b5fa 100644 --- a/print_designer/public/js/print_designer/PropertiesPanelState.js +++ b/print_designer/public/js/print_designer/PropertiesPanelState.js @@ -389,8 +389,36 @@ export const createPropertiesPanel = () => { }, }, [ - pageInput("Height", "page_height", "height", { parentBorderTop: true }), - pageInput("Width", "page_width", "width"), + pageInput("Height", "page_height", "height", { + parentBorderTop: true, + condtional: () => MainStore.mode == "editing", + }), + pageInput("Width", "page_width", "width", { + condtional: () => MainStore.mode == "editing", + }), + ], + [ + { + label: "Delete Page", + name: "deletePage", + isLabelled: true, + flex: "auto", + condtional: () => MainStore.activePage, + reactiveObject: () => MainStore.activePage, + button: { + label: "Delete Page", + size: "sm", + style: "secondary", + margin: 15, + onClick: (e, field) => { + ElementStore.Elements.splice( + ElementStore.Elements.indexOf(MainStore.activePage), + 1 + ); + e.target.blur(); + }, + }, + }, ], ], }); @@ -410,11 +438,9 @@ export const createPropertiesPanel = () => { ], }); MainStore.propertiesPanel.push({ - title: "PDF Settings", + title: "Header / Footer", sectionCondtional: () => - MainStore.mode == "pdfSetup" && - !MainStore.getCurrentElementsId.length && - MainStore.activeControl === "mouse-pointer", + !MainStore.getCurrentElementsId.length && MainStore.activeControl === "mouse-pointer", fields: [ [ pageInput("Header", "page_header", "headerHeight"), @@ -422,6 +448,112 @@ export const createPropertiesPanel = () => { ], ], }); + + MainStore.propertiesPanel.push({ + title: "Select Pages", + sectionCondtional: () => + MainStore.mode != "editing" && + MainStore.activePage && + !MainStore.getCurrentElementsId.length && + MainStore.activeControl === "mouse-pointer", + fields: [ + [ + { + label: "First", + name: "firstPage", + isLabelled: true, + condtional: () => MainStore.activePage, + reactiveObject: () => MainStore.activePage, + button: { + label: "First", + size: "sm", + style: () => (MainStore.activePage.firstPage ? "primary" : "secondary"), + margin: 15, + onClick: (e, field) => { + MainStore.activePage.firstPage = !MainStore.activePage.firstPage; + if (MainStore.activePage.firstPage) { + ElementStore.Elements.forEach((element) => { + if (element == MainStore.activePage) return; + element.firstPage = false; + }); + } + e.target.blur(); + }, + }, + }, + { + label: "Odd", + name: "oddPages", + isLabelled: true, + condtional: () => MainStore.activePage, + reactiveObject: () => MainStore.activePage, + button: { + label: "Odd", + style: () => (MainStore.activePage.oddPage ? "primary" : "secondary"), + margin: 15, + size: "sm", + onClick: (e, field) => { + MainStore.activePage.oddPage = !MainStore.activePage.oddPage; + if (MainStore.activePage.oddPage) { + ElementStore.Elements.forEach((element) => { + if (element == MainStore.activePage) return; + element.oddPage = false; + }); + } + e.target.blur(); + }, + }, + }, + { + label: "Even", + name: "evenPages", + isLabelled: true, + condtional: () => MainStore.activePage, + reactiveObject: () => MainStore.activePage, + button: { + label: "Even", + style: () => (MainStore.activePage.evenPage ? "primary" : "secondary"), + margin: 15, + size: "sm", + onClick: (e, field) => { + MainStore.activePage.evenPage = !MainStore.activePage.evenPage; + if (MainStore.activePage.evenPage) { + ElementStore.Elements.forEach((element) => { + if (element == MainStore.activePage) return; + element.evenPage = false; + }); + } + e.target.blur(); + }, + }, + }, + { + label: "Last", + name: "lastPages", + isLabelled: true, + condtional: () => MainStore.activePage, + reactiveObject: () => MainStore.activePage, + button: { + label: "Last", + style: () => (MainStore.activePage.lastPage ? "primary" : "secondary"), + margin: 15, + size: "sm", + onClick: (e, field) => { + MainStore.activePage.lastPage = !MainStore.activePage.lastPage; + if (MainStore.activePage.lastPage) { + ElementStore.Elements.forEach((element) => { + if (element == MainStore.activePage) return; + element.lastPage = false; + }); + } + e.target.blur(); + }, + }, + }, + ], + ], + }); + MainStore.propertiesPanel.push({ title: "Transform", sectionCondtional: () => MainStore.getCurrentElementsId.length === 1, @@ -442,6 +574,9 @@ export const createPropertiesPanel = () => { labelDirection: "column", condtional: () => { const currentEl = MainStore.getCurrentElementsValues[0]; + if (currentEl.isElementOverlapping) { + return false; + } if ( currentEl?.type === "table" || (currentEl.type === "text" && diff --git a/print_designer/public/js/print_designer/components/base/BaseDynamicText.vue b/print_designer/public/js/print_designer/components/base/BaseDynamicText.vue index 0eda607..f45e486 100644 --- a/print_designer/public/js/print_designer/components/base/BaseDynamicText.vue +++ b/print_designer/public/js/print_designer/components/base/BaseDynamicText.vue @@ -69,7 +69,7 @@ - + diff --git a/print_designer/public/js/print_designer/components/layout/AppCanvas.vue b/print_designer/public/js/print_designer/components/layout/AppCanvas.vue index 8052de6..07058ad 100644 --- a/print_designer/public/js/print_designer/components/layout/AppCanvas.vue +++ b/print_designer/public/js/print_designer/components/layout/AppCanvas.vue @@ -3,34 +3,73 @@
-
- - + +
+ + +
+ + diff --git a/print_designer/public/js/print_designer/components/layout/AppPdfSetup.vue b/print_designer/public/js/print_designer/components/layout/AppPdfSetup.vue deleted file mode 100644 index f927e54..0000000 --- a/print_designer/public/js/print_designer/components/layout/AppPdfSetup.vue +++ /dev/null @@ -1,225 +0,0 @@ - - - - - diff --git a/print_designer/public/js/print_designer/components/layout/AppPropertiesPanel.vue b/print_designer/public/js/print_designer/components/layout/AppPropertiesPanel.vue index 8330282..1be0677 100644 --- a/print_designer/public/js/print_designer/components/layout/AppPropertiesPanel.vue +++ b/print_designer/public/js/print_designer/components/layout/AppPropertiesPanel.vue @@ -12,19 +12,10 @@
-
- -
{ element.startX -= finalValue.value - object[property]; @@ -351,15 +352,23 @@ const handleBlur = ({ }); if (!object.isStyle && object.UOM == MainStore.page.UOM) { if (property == "marginTop") { - ElementStore.Elements.forEach((element) => { - element.startY -= convertedValue.value - object[property]; + ElementStore.Elements.forEach((page) => { + page.childrens.forEach((element) => { + element.startY -= convertedValue.value - object[property]; + }); + page.header[0].height += object[property] - convertedValue.value; + page.footer[0].startY += object[property] - convertedValue.value; }); MainStore.page.headerHeight += object[property] - convertedValue.value; - MainStore.page.footerHeight += object[property] - convertedValue.value; } else if (property == "marginLeft") { ElementStore.Elements.forEach((element) => { element.startX -= convertedValue.value - object[property]; }); + } else if (property == "marginBottom") { + ElementStore.Elements.forEach((page) => { + page.footer[0].height += object[property] - convertedValue.value; + MainStore.page.footerHeight += object[property] - convertedValue.value; + }); } if (["width", "height"].indexOf(property)) { let propertyValue = useChangeValueUnit({ diff --git a/print_designer/public/js/print_designer/composables/AttachKeyBindings.js b/print_designer/public/js/print_designer/composables/AttachKeyBindings.js index 3ebf97b..105c88e 100644 --- a/print_designer/public/js/print_designer/composables/AttachKeyBindings.js +++ b/print_designer/public/js/print_designer/composables/AttachKeyBindings.js @@ -9,11 +9,7 @@ export function useAttachKeyBindings() { function updateStartXY(axis, value) { MainStore.getCurrentElementsValues.forEach((element) => { let restrict; - if (Array.isArray(element.parent)) { - restrict = MainStore.mainContainer.getBoundingClientRect(); - } else { - restrict = element.parent.DOMRef.getBoundingClientRect(); - } + restrict = element.parent.DOMRef.getBoundingClientRect(); if (element[`start${axis}`] + value <= -1) { element[`start${axis}`] = -1; } else if ( @@ -32,12 +28,7 @@ export function useAttachKeyBindings() { } function updateWidthHeight(key, value) { MainStore.getCurrentElementsValues.forEach((element) => { - let restrict; - if (Array.isArray(element.parent)) { - restrict = MainStore.mainContainer.getBoundingClientRect(); - } else { - restrict = element.parent.DOMRef.getBoundingClientRect(); - } + let restrict = element.parent.DOMRef.getBoundingClientRect(); if (element[key] + value <= -1) { element[key] = -1; } else if ( @@ -54,12 +45,13 @@ export function useAttachKeyBindings() { const handleKeyDown = async (e) => { MainStore.isAltKey = e.altKey; MainStore.isShiftKey = e.shiftKey; - if (e.target !== document.body || MainStore.mode != "editing" || MainStore.openModal) - return; + if (e.target !== document.body || MainStore.openModal) return; if (e.ctrlKey || e.metaKey) { if (["a", "A"].indexOf(e.key) != -1) { - ElementStore.Elements.forEach((element) => { - MainStore.currentElements[element.id] = element; + ElementStore.Elements.forEach((page) => { + page.childrens.forEach((element) => { + MainStore.currentElements[element.id] = element; + }); }); } else if (!e.repeat && ["s", "S"].indexOf(e.key) != -1) { await ElementStore.saveElements(); diff --git a/print_designer/public/js/print_designer/composables/Draggable.js b/print_designer/public/js/print_designer/composables/Draggable.js index dc91858..17b09be 100644 --- a/print_designer/public/js/print_designer/composables/Draggable.js +++ b/print_designer/public/js/print_designer/composables/Draggable.js @@ -17,7 +17,6 @@ export function useDraggable({ if (interact.isSet(element["DOMRef"]) && interact(element["DOMRef"]).draggable().enabled) return; const MainStore = useMainStore(); - const ElementStore = useElementStore(); let elementPreviousZAxis; let top, left, bottom, right; if (typeof restrict != "string") { @@ -57,34 +56,6 @@ export function useDraggable({ if (element.DOMRef.className == "modal-dialog modal-sm") { return; } - if ( - !e.dropzone && - !Array.isArray(e.target.piniaElementRef.parent) && - !MainStore.lastCloned - ) { - let splicedElement; - let currentRect = e.target.getBoundingClientRect(); - let canvasRect = MainStore.mainContainer.getBoundingClientRect(); - let currentParent = e.target.piniaElementRef.parent; - if (Array.isArray(currentParent)) { - splicedElement = currentParent.splice(e.target.piniaElementRef.index, 1)[0]; - } else { - splicedElement = currentParent.childrens.splice( - e.target.piniaElementRef.index, - 1 - )[0]; - } - splicedElement = { ...splicedElement }; - splicedElement.id = frappe.utils.get_random(10); - splicedElement.startX = currentRect.left - canvasRect.left; - splicedElement.startY = currentRect.top - canvasRect.top; - splicedElement.parent = ElementStore.Elements; - recursiveChildrens({ element: splicedElement, isClone: false }); - ElementStore.Elements.push(splicedElement); - let droppedElement = new Object(); - droppedElement[splicedElement.id] = splicedElement; - MainStore.isDropped = droppedElement; - } checkUpdateElementOverlapping(element); }); return; diff --git a/print_designer/public/js/print_designer/composables/DropZone.js b/print_designer/public/js/print_designer/composables/DropZone.js index 6846a9a..70397ab 100644 --- a/print_designer/public/js/print_designer/composables/DropZone.js +++ b/print_designer/public/js/print_designer/composables/DropZone.js @@ -2,13 +2,11 @@ import interact from "@interactjs/interact"; import "@interactjs/actions/drop"; import "@interactjs/auto-start"; import "@interactjs/modifiers"; -import { useElementStore } from "../store/ElementStore"; import { useMainStore } from "../store/MainStore"; import { recursiveChildrens } from "../utils"; export function useDropZone({ element }) { const MainStore = useMainStore(); - const ElementStore = useElementStore(); if (interact.isSet(element["DOMRef"]) && interact(element["DOMRef"]).dropzone().enabled) return; interact(element.DOMRef).dropzone({ @@ -20,22 +18,13 @@ export function useDropZone({ element }) { let currentRect = event.draggable.target.getBoundingClientRect(); let dropRect = event.dropzone.target.getBoundingClientRect(); if (currentDROP === currentRef.parent) return; - let splicedElement; - if (Array.isArray(currentRef.parent)) { - splicedElement = currentRef.parent.splice(currentRef.index, 1)[0]; - } else { - splicedElement = currentRef.parent.childrens.splice(currentRef.index, 1)[0]; - } + let splicedElement = currentRef.parent.childrens.splice(currentRef.index, 1)[0]; splicedElement = { ...splicedElement }; splicedElement.startX = currentRect.left - dropRect.left; splicedElement.startY = currentRect.top - dropRect.top; splicedElement.parent = currentDROP; recursiveChildrens({ element: splicedElement, isClone: false }); - if (Array.isArray(currentDROP)) { - currentDROP.push(splicedElement); - } else { - currentDROP.childrens.push(splicedElement); - } + currentDROP.childrens.push(splicedElement); let droppedElement = new Object(); droppedElement[splicedElement.id] = splicedElement; MainStore.isDropped = droppedElement; diff --git a/print_designer/public/js/print_designer/composables/Element.js b/print_designer/public/js/print_designer/composables/Element.js index 3ff1485..9c168f0 100644 --- a/print_designer/public/js/print_designer/composables/Element.js +++ b/print_designer/public/js/print_designer/composables/Element.js @@ -1,15 +1,19 @@ import { useMainStore } from "../store/MainStore"; -import { useElementStore } from "../store/ElementStore"; import { useDraggable } from "./Draggable"; import { useResizable } from "./Resizable"; import { useDropZone } from "./DropZone"; import { watch, markRaw } from "vue"; import interact from "@interactjs/interact"; -import { changeDraggable, changeResizable, changeDropZone, getSnapPointsAndEdges } from "../utils"; +import { + changeDraggable, + changeResizable, + changeDropZone, + getSnapPointsAndEdges, + getParentPage, +} from "../utils"; export function useElement({ draggable = true, resizable = true }) { const MainStore = useMainStore(); - const ElementStore = useElementStore(); const setReferance = (element) => (el) => { element.DOMRef = markRaw(el); el.piniaElementRef = element; @@ -18,8 +22,8 @@ export function useElement({ draggable = true, resizable = true }) { if (!element) return; element.index = index; if (element.DOMRef) return; - setReferance(element)(DOMElement); if (element && DOMElement) { + setReferance(element)(DOMElement); draggable && setDraggable(element); resizable && setResizable(element); const { @@ -32,7 +36,9 @@ export function useElement({ draggable = true, resizable = true }) { } = getSnapPointsAndEdges(element); element.snapPoints = [rowSnapPoint, columnSnapPoint]; element.snapEdges = [leftSnapEdge, rightSnapEdge, topSnapEdge, bottomSnapEdge]; - element.type == "rectangle" && setDropZone(element); + if (element.type == "rectangle" || element.type == "page") { + setDropZone(element); + } element && changeResizable(element); element && changeDraggable(element); element && changeDropZone(element); @@ -59,10 +65,12 @@ export function useElement({ draggable = true, resizable = true }) { () => { if (!element) return; if (MainStore.activeControl == "mouse-pointer") { - element.isDraggable = true; + if (element.type != "page") { + element.isDraggable = true; + } if (!MainStore.isAltKey) { element.isResizable = true; - if (element.type == "rectangle") { + if (element.type == "rectangle" || element.type == "page") { element.isDropZone = true; } } else { @@ -97,9 +105,15 @@ export function useElement({ draggable = true, resizable = true }) { } }; const setDraggable = (element) => { + if (element.relativeContainer) return; + const pageParent = getParentPage(element); + if (!pageParent) { + return; + } + const parentDOMRef = pageParent.DOMRef; useDraggable({ element, - restrict: MainStore.mainContainer, + restrict: parentDOMRef, dragMoveListener: (e) => { if (e.metaKey || e.ctrlKey) { e.interactable.options.drag.modifiers[0].disable(); @@ -127,12 +141,9 @@ export function useElement({ draggable = true, resizable = true }) { e.stopImmediatePropagation(); }, dragStartListener: (e) => { - let parentRect; - if (Array.isArray(element.parent)) { - parentRect = MainStore.mainContainer.getBoundingClientRect(); - } else { - parentRect = element.parent.DOMRef.getBoundingClientRect(); - } + const parentRect = + element.parent.DOMRef?.getBoundingClientRect() || + parentDOMRef.getBoundingClientRect(); const elementRect = element.DOMRef.getBoundingClientRect(); let offsetRect = MainStore.getCurrentElementsValues.reduce( (offset, currentElement) => { @@ -171,33 +182,42 @@ export function useElement({ draggable = true, resizable = true }) { }; elementPreviousZAxis = element.style.zIndex || 0; element.style.zIndex = 9999; - - e.interactable.options.drag.modifiers[0].options.restriction = { + const restrictionRect = { top: parentRect.top + restrictRect.top, left: parentRect.left + restrictRect.left, right: parentRect.right - restrictRect.right, bottom: parentRect.bottom - restrictRect.bottom, }; + if (MainStore.mode == "editing" && element.parent.type == "page") { + restrictionRect.top += MainStore.page.headerHeight; + restrictionRect.bottom -= MainStore.page.footerHeight; + } + e.interactable.options.drag.modifiers[0].options.restriction = restrictionRect; }, }); }; const setResizable = (element) => { + const pageParent = getParentPage(element); + if (!pageParent) { + return; + } + const parentDOMRef = pageParent.DOMRef; useResizable({ element, - restrict: MainStore.mainContainer, + restrict: parentDOMRef, resizeStartListener: (e) => { - let parentRect; - if (Array.isArray(element.parent)) { - parentRect = MainStore.mainContainer.getBoundingClientRect(); - } else { - parentRect = element.parent.DOMRef.getBoundingClientRect(); - } - e.interactable.options.resize.modifiers[0].options.outer = { + let parentRect = element.parent.DOMRef.getBoundingClientRect(); + const restrictionRect = { top: parentRect.top, left: parentRect.left, right: parentRect.right, bottom: parentRect.bottom, }; + if (MainStore.mode == "editing" && element.parent.type == "page") { + restrictionRect.top += MainStore.page.headerHeight; + restrictionRect.bottom -= MainStore.page.footerHeight; + } + e.interactable.options.resize.modifiers[0].options.outer = restrictionRect; if (!element.childrens || !element.childrens.length) return; let offsetRect = element.childrens.reduce( (offset, currentElement) => { @@ -235,7 +255,7 @@ export function useElement({ draggable = true, resizable = true }) { element.startY = (element.startY || 0) + e.deltaRect.top; element.width = (element.width || 0) - e.deltaRect.left + e.deltaRect.right; element.height = (element.height || 0) - e.deltaRect.top + e.deltaRect.bottom; - if (element.type == "rectangle") { + if (element.type == "rectangle" || element.type == "page") { element.childrens && element.childrens.forEach((childEl) => { childEl.startX -= e.deltaRect.left; diff --git a/print_designer/public/js/print_designer/composables/MarqueeSelectionTool.js b/print_designer/public/js/print_designer/composables/MarqueeSelectionTool.js index 3c65341..cbc47ac 100644 --- a/print_designer/public/js/print_designer/composables/MarqueeSelectionTool.js +++ b/print_designer/public/js/print_designer/composables/MarqueeSelectionTool.js @@ -37,6 +37,7 @@ export function useMarqueeSelection() { if (e.buttons != 1) return; if (e.target.id == "canvas" && MainStore.activeControl != "mouse-pointer") { MainStore.setActiveControl("MousePointer"); + MainStore.activePage = null; MainStore.isMarqueeActive = true; } if (!MainStore[beforeDraw]) return; @@ -95,38 +96,34 @@ export function useMarqueeSelection() { }); } - const a = { - x: parameters.startX - canvas.getBoundingClientRect().left, - y: parameters.startY - canvas.getBoundingClientRect().top, + const canvas = { + x: parameters.startX, + y: parameters.startY, width: Math.abs(parameters.width), - height: Math.abs(parameters.height) + canvas.scrollTop, + height: Math.abs(parameters.height), }; - const mainContainerRect = MainStore.mainContainer.getBoundingClientRect(); - a.x -= - mainContainerRect.x - MainStore.toolbarWidth > 0 - ? mainContainerRect.x - MainStore.toolbarWidth - : 0; - a.y -= - mainContainerRect.y - MainStore.page.marginTop <= 110 - ? MainStore.page.marginTop + 50 - : mainContainerRect.y - MainStore.page.marginTop - 110; - for (const value of ElementStore.Elements) { - const { id, startX, startY, width, height, DOMRef } = value; - const b = { - id, - x: startX, - y: startY, - width, - height, - DOMRef, - }; - - if (isInBounds(a, b)) { - inBounds.push(DOMRef); - if ((e.metaKey || e.ctrlKey) && e.shiftKey) { - delete MainStore.currentElements[id]; - } else { - MainStore.currentElements[id] = value; + for (const page of ElementStore.Elements) { + const pageRect = page.DOMRef.getBoundingClientRect(); + a = { ...canvas }; + a.x -= pageRect.x; + a.y -= pageRect.y; + for (const element of page.childrens) { + const { id, startX, startY, width, height, DOMRef } = element; + const b = { + id, + x: startX, + y: startY, + width, + height, + DOMRef, + }; + if (!element.relativeContainer && isInBounds(a, b)) { + inBounds.push(DOMRef); + if ((e.metaKey || e.ctrlKey) && e.shiftKey) { + delete MainStore.currentElements[id]; + } else { + MainStore.currentElements[id] = element; + } } } } diff --git a/print_designer/public/js/print_designer/composables/Resizable.js b/print_designer/public/js/print_designer/composables/Resizable.js index 99af2f9..61de2a1 100644 --- a/print_designer/public/js/print_designer/composables/Resizable.js +++ b/print_designer/public/js/print_designer/composables/Resizable.js @@ -4,7 +4,7 @@ import "@interactjs/auto-start"; import "@interactjs/modifiers"; import { useMainStore } from "../store/MainStore"; import { useElementStore } from "../store/ElementStore"; -import { recursiveChildrens, checkUpdateElementOverlapping } from "../utils"; +import { recursiveChildrens, checkUpdateElementOverlapping, getParentPage } from "../utils"; export function useResizable({ element, @@ -19,15 +19,18 @@ export function useResizable({ } const MainStore = useMainStore(); const ElementStore = useElementStore(); + const edges = { + bottom: ".resize-bottom", + }; + if (!element.relativeContainer) { + edges.left = ".resize-left"; + edges.right = ".resize-right"; + edges.top = ".resize-top"; + } interact(element.DOMRef) .resizable({ ignoreFrom: ".resizer", - edges: { - left: ".resize-left", - right: ".resize-right", - bottom: ".resize-bottom", - top: ".resize-top", - }, + edges: edges, modifiers: [ interact.modifiers.restrictEdges(), interact.modifiers.snapEdges({ @@ -48,14 +51,16 @@ export function useResizable({ if (element.parent == e.target.piniaElementRef.parent) return; if ( !e.dropzone && - !Array.isArray(e.target.piniaElementRef.parent) && + e.target.piniaElementRef.parent.type != "page" && !MainStore.lastCloned ) { let splicedElement; let currentRect = e.target.getBoundingClientRect(); - let canvasRect = MainStore.mainContainer.getBoundingClientRect(); + let canvasRect = getParentPage( + e.target.piniaElementRef.parent + ).DOMRef.getBoundingClientRect(); let currentParent = e.target.piniaElementRef.parent; - if (Array.isArray(currentParent)) { + if (currentParent.type == "page") { splicedElement = currentParent.splice( e.target.piniaElementRef.index, 1 diff --git a/print_designer/public/js/print_designer/defaultObjects.js b/print_designer/public/js/print_designer/defaultObjects.js index 74dafd1..ef6ad21 100644 --- a/print_designer/public/js/print_designer/defaultObjects.js +++ b/print_designer/public/js/print_designer/defaultObjects.js @@ -1,11 +1,8 @@ import { useMainStore } from "./store/MainStore"; -import { useElementStore } from "./store/ElementStore"; export const createRectangle = (cordinates, parent = null) => { - const ElementStore = useElementStore(); const MainStore = useMainStore(); - if (parent === null) parent = ElementStore.Elements; let id = frappe.utils.get_random(10); if (cordinates instanceof MouseEvent) { cordinates = { @@ -35,15 +32,13 @@ export const createRectangle = (cordinates, parent = null) => { classes: [], }; - Array.isArray(parent) ? parent.push(newRectangle) : parent.childrens.push(newRectangle); + parent.childrens?.push(newRectangle); MainStore.lastCreatedElement = newRectangle; return newRectangle; }; export const createImage = (cordinates, parent = null) => { - const ElementStore = useElementStore(); const MainStore = useMainStore(); - if (parent === null) parent = ElementStore.Elements; let id = frappe.utils.get_random(10); if (cordinates instanceof MouseEvent) { cordinates = { @@ -74,15 +69,13 @@ export const createImage = (cordinates, parent = null) => { classes: [], }; - Array.isArray(parent) ? parent.push(newImage) : parent.childrens.push(newImage); + parent.childrens?.push(newImage) || parent.childrens.push(newImage); MainStore.lastCreatedElement = newImage; return newImage; }; export const createBarcode = (cordinates, parent = null) => { - const ElementStore = useElementStore(); const MainStore = useMainStore(); - if (parent === null) parent = ElementStore.Elements; let id = frappe.utils.get_random(10); if (cordinates instanceof MouseEvent) { cordinates = { @@ -117,15 +110,13 @@ export const createBarcode = (cordinates, parent = null) => { classes: [], }; - Array.isArray(parent) ? parent.push(newBarcode) : parent.childrens.push(newBarcode); + parent.childrens?.push(newBarcode) || parent.childrens.push(newBarcode); MainStore.lastCreatedElement = newBarcode; return newBarcode; }; export const createTable = (cordinates, parent = null) => { - const ElementStore = useElementStore(); const MainStore = useMainStore(); - if (parent === null) parent = ElementStore.Elements; let id = frappe.utils.get_random(10); if (cordinates instanceof MouseEvent) { cordinates = { @@ -164,16 +155,14 @@ export const createTable = (cordinates, parent = null) => { classes: [], }; - Array.isArray(parent) ? parent.push(newTable) : parent.childrens.push(newTable); + parent.childrens?.push(newTable) || parent.childrens.push(newTable); MainStore.lastCreatedElement = newTable; return newTable; }; export const createText = (cordinates, parent = null) => { - const ElementStore = useElementStore(); const MainStore = useMainStore(); - if (parent === null) parent = ElementStore.Elements; let id = frappe.utils.get_random(10); if (cordinates instanceof MouseEvent) { cordinates = { @@ -207,15 +196,13 @@ export const createText = (cordinates, parent = null) => { style: {}, classes: [], }; - Array.isArray(parent) ? parent.push(newStaticText) : parent.childrens.push(newStaticText); + parent.childrens?.push(newStaticText) || parent.childrens.push(newStaticText); MainStore.lastCreatedElement = newStaticText; return newStaticText; }; export const createDynamicText = (cordinates, parent = null) => { - const ElementStore = useElementStore(); const MainStore = useMainStore(); - if (parent === null) parent = ElementStore.Elements; let id = frappe.utils.get_random(10); if (cordinates instanceof MouseEvent) { cordinates = { @@ -252,7 +239,7 @@ export const createDynamicText = (cordinates, parent = null) => { heightType: "auto", classes: [], }; - Array.isArray(parent) ? parent.push(newDynamicText) : parent.childrens.push(newDynamicText); + parent.childrens?.push(newDynamicText) || parent.childrens.push(newDynamicText); MainStore.lastCreatedElement = newDynamicText; return newDynamicText; }; diff --git a/print_designer/public/js/print_designer/icons/IconsUse.vue b/print_designer/public/js/print_designer/icons/IconsUse.vue index 70f3aac..d4d1c90 100644 --- a/print_designer/public/js/print_designer/icons/IconsUse.vue +++ b/print_designer/public/js/print_designer/icons/IconsUse.vue @@ -29,7 +29,7 @@ const props = defineProps({ default: 0, }, color: { - default: "#000000", + default: "var(--neutral)", }, class: { type: String, diff --git a/print_designer/public/js/print_designer/store/ElementStore.js b/print_designer/public/js/print_designer/store/ElementStore.js index db4e30e..8761b86 100644 --- a/print_designer/public/js/print_designer/store/ElementStore.js +++ b/print_designer/public/js/print_designer/store/ElementStore.js @@ -8,13 +8,20 @@ import { createTable, createBarcode, } from "../defaultObjects"; -import { handlePrintFonts, setCurrentElement } from "../utils"; +import { + handlePrintFonts, + setCurrentElement, + createHeaderFooterElement, + getParentPage, +} from "../utils"; import html2canvas from "html2canvas"; export const useElementStore = defineStore("ElementStore", { state: () => ({ Elements: new Array(), + Headers: new Array(), + Footers: new Array(), }), actions: { createNewObject(event, element) { @@ -53,22 +60,50 @@ export const useElementStore = defineStore("ElementStore", { body: [], footer: [], }; - // {childrens: []} is passed because we update parent in createRectangle function. let headerElements = []; let bodyElements = []; let footerElements = []; - // WARNING: 2 lines below are for debugging purpose only. - // this.Elements.length = 0; - // headerElements = bodyElements = footerElements = this.Elements; if (header) { - layout.header = this.computeRowLayout(header, headerElements, "header"); + const headerArray = header.map((h) => { + h.childrens = this.computeRowLayout(h.childrens, headerElements, "header"); + return h; + }); + layout.header = { + firstPage: headerArray.find((h) => h.firstPage).childrens, + oddPage: headerArray.find((h) => h.oddPage).childrens, + evenPage: headerArray.find((h) => h.evenPage).childrens, + lastPage: headerArray.find((h) => h.lastPage).childrens, + }; } // it will throw error if body is empty so no need to check here - layout.body = this.computeRowLayout(body, bodyElements, "body"); + layout.body = body.map((b) => { + b.childrens = this.computeRowLayout(b.childrens, bodyElements, "body"); + return b; + }); if (footer) { - layout.footer = this.computeRowLayout(footer, footerElements, "footer"); + const footerArray = footer.map((f) => { + f.childrens = this.computeRowLayout(f.childrens, footerElements, "footer"); + return f; + }); + layout.footer = { + firstPage: footerArray.find((h) => h.firstPage).childrens, + oddPage: footerArray.find((h) => h.oddPage).childrens, + evenPage: footerArray.find((h) => h.evenPage).childrens, + lastPage: footerArray.find((h) => h.lastPage).childrens, + }; } - + // WARNING: lines below are for debugging purpose only. + // this.Elements.length = 0; + // this.Headers.length = 0; + // this.Footers.length = 0; + // this.Headers.push(...layout.header); + // this.Elements.push(...layout.body); + // this.Footers.push(...layout.footer); + // this.Elements.forEach((page, index) => { + // page.header = [createHeaderFooterElement(this.getHeaderObject(index).childrens, "header")]; + // page.footer = [createHeaderFooterElement(this.getFooterObject(index).childrens, "footer")] + // }); + // End of debugging code objectToSave.print_designer_print_format = JSON.stringify(layout); // update fonts in store @@ -180,7 +215,6 @@ export const useElementStore = defineStore("ElementStore", { async saveElements() { const MainStore = useMainStore(); if (this.checkIfAnyTableIsEmpty()) return; - if (MainStore.mode == "preview") return; let is_standard = await frappe.db.get_value( "Print Format", MainStore.printDesignName, @@ -254,33 +288,34 @@ export const useElementStore = defineStore("ElementStore", { return false; }, computeMainLayout() { - const MainStore = useMainStore(); - elements = [...this.Elements]; - elements.sort((a, b) => { - return a.startY < b.startY ? -1 : 1; + let header = []; + let body = []; + let footer = []; + const pages = [...this.Elements]; + const headerArray = [...this.Headers]; + const footerArray = [...this.Footers]; + headerArray.forEach((h) => { + const headerCopy = { ...h }; + h.childrens = this.cleanUpElementsForSave(h.childrens, "header") || []; + header.push(headerCopy); + }); + pages.forEach((page) => { + const pageCopy = { ...page }; + delete pageCopy.DOMRef; + delete pageCopy.parent; + delete pageCopy.header; + delete pageCopy.footer; + pageCopy.childrens.sort((a, b) => { + return a.startY < b.startY ? -1 : 1; + }); + pageCopy.childrens = this.cleanUpElementsForSave(pageCopy.childrens, "body"); + body.push(pageCopy); + }); + footerArray.forEach((f) => { + const footerCopy = { ...f }; + footerCopy.childrens = this.cleanUpElementsForSave(f.childrens, "footer") || []; + footer.push(footerCopy); }); - const findLastHeaderEl = (el) => { - return el.startY >= MainStore.page.headerHeight; - }; - const findFirstFooterEl = (el) => { - return ( - el.startY >= - MainStore.page.height - - MainStore.page.footerHeight - - MainStore.page.marginTop - - MainStore.page.marginBottom - ); - }; - let headerIndex = elements.findIndex((el) => findLastHeaderEl(el)); - headerIndex == -1 && (headerIndex = elements.length); - const header = this.cleanUpElementsForSave(elements.splice(0, headerIndex), "header"); - let footerIndex = elements.findIndex((el) => findFirstFooterEl(el)); - footerIndex == -1 && (footerIndex = elements.length); - const footer = this.cleanUpElementsForSave( - elements.splice(footerIndex, elements.length - footerIndex), - "footer" - ); - const body = this.cleanUpElementsForSave(elements, "body"); return { header, body, footer }; }, // TODO: Refactor this function @@ -345,11 +380,11 @@ export const useElementStore = defineStore("ElementStore", { rowElements.push(wrapper); } rowElements.sort((a, b) => (a.startY < b.startY ? -1 : 1)); - if (type == "header") { + if (type == "header" && rowElements.length) { const lastHeaderRow = rowElements[rowElements.length - 1]; lastHeaderRow.height = MainStore.page.headerHeight - MainStore.page.marginTop - lastHeaderRow.startY; - } else if (type == "footer") { + } else if (type == "footer" && rowElements.length) { const lastHeaderRow = rowElements[rowElements.length - 1]; lastHeaderRow.height = MainStore.page.height - @@ -456,7 +491,6 @@ export const useElementStore = defineStore("ElementStore", { auto: __("in table, auto layout failed"), }); message += messageType[type]; - MainStore.mode = "pdfSetup"; frappe.show_alert( { message: message, @@ -656,19 +690,19 @@ export const useElementStore = defineStore("ElementStore", { childrensSave(element, printFonts = null) { let saveEl = { ...element }; delete saveEl.DOMRef; - delete saveEl.index; delete saveEl.snapPoints; delete saveEl.snapEdges; delete saveEl.parent; this.cleanUpDynamicContent(saveEl); if (saveEl.type == "table") { + saveEl.table = { ...saveEl.table }; delete saveEl.table.childfields; delete saveEl.table.default_layout; } if (printFonts && ["text", "table"].indexOf(saveEl.type) != -1) { handlePrintFonts(saveEl, printFonts); } - if (saveEl.type == "rectangle") { + if (saveEl.type == "rectangle" || saveEl.type == "page") { const childrensArray = saveEl.childrens; saveEl.childrens = []; childrensArray.forEach((el) => { @@ -731,7 +765,7 @@ export const useElementStore = defineStore("ElementStore", { createWrapperElement(dimensions, parent) { const MainStore = useMainStore(); const coordinates = {}; - if (Array.isArray(parent)) { + if (parent.type == "page") { coordinates["startY"] = dimensions.top; coordinates["pageY"] = dimensions.top; coordinates["startX"] = 0; @@ -778,7 +812,7 @@ export const useElementStore = defineStore("ElementStore", { return; }, updateRowChildrenDimensions(wrapper, children, parent) { - if (Array.isArray(parent)) { + if (parent.type == "page") { children.forEach((el) => { el.startY -= wrapper.startY; }); @@ -822,7 +856,7 @@ export const useElementStore = defineStore("ElementStore", { createRowWrapperElement(dimension, currentRow, parent) { const MainStore = useMainStore(); const coordinates = {}; - if (Array.isArray(parent)) { + if (parent.type == "page") { coordinates["startY"] = dimension.top; coordinates["pageY"] = dimension.top; coordinates["startX"] = 0; @@ -1054,7 +1088,7 @@ export const useElementStore = defineStore("ElementStore", { element.isDraggable = true; element.isResizable = true; this.handleDynamicContent(element); - if (element.type == "rectangle") { + if (element.type == "rectangle" || element.type == "page") { element.isDropZone = true; const childrensArray = element.childrens; element.childrens = []; @@ -1091,35 +1125,37 @@ export const useElementStore = defineStore("ElementStore", { }); return; }, - async loadElements(printDesignName) { - frappe.dom.freeze(__("Loading Print Format")); - const printFormat = await frappe.db.get_value("Print Format", printDesignName, [ - "print_designer_header", - "print_designer_body", - "print_designer_after_table", - "print_designer_footer", - "print_designer_settings", - ]); - let ElementsHeader = JSON.parse(printFormat.message.print_designer_header); - let ElementsBody = JSON.parse(printFormat.message.print_designer_body); - let ElementsAfterTable = JSON.parse(printFormat.message.print_designer_after_table); - let ElementsFooter = JSON.parse(printFormat.message.print_designer_footer); - let settings = JSON.parse(printFormat.message.print_designer_settings); - this.loadSettings(settings); - this.Elements = [ - ...(ElementsHeader || []), - ...(ElementsBody || []), - ...(ElementsAfterTable || []), - ...(ElementsFooter || []), - ]; - this.Elements.map((element) => { + getHeaderObject(index) { + if (index == 0) { + return this.Headers.find((header) => header.firstPage == true); + } else if (index == this.Elements.length - 1) { + return this.Headers.find((header) => header.lastPage == true); + } else if (index % 2 != 0) { + return this.Headers.find((header) => header.oddPage == true); + } else { + return this.Headers.find((header) => header.evenPage == true); + } + }, + getFooterObject(index) { + if (index == 0) { + return this.Footers.find((footer) => footer.firstPage == true); + } else if (index == this.Elements.length - 1) { + return this.Footers.find((footer) => footer.lastPage == true); + } else if (index % 2 != 0) { + return this.Footers.find((footer) => footer.oddPage == true); + } else { + return this.Footers.find((footer) => footer.evenPage == true); + } + }, + setElementProperties(parent) { + parent.childrens.map((element) => { element.DOMRef = null; - element.parent = this.Elements; + element.parent = parent; delete element.printY; element.isDraggable = true; element.isResizable = true; this.handleDynamicContent(element); - if (element.type == "rectangle") { + if (element.type == "rectangle" || element.type == "page") { element.isDropZone = true; if (element.childrens.length) { let childrensArray = element.childrens; @@ -1133,6 +1169,55 @@ export const useElementStore = defineStore("ElementStore", { } return element; }); + }, + createPageElement(element, type) { + return { + type: "page", + childrens: [...element], + firstPage: true, + oddPage: true, + evenPage: true, + lastPage: true, + DOMRef: null, + }; + }, + async loadElements(printDesignName) { + frappe.dom.freeze(__("Loading Print Format")); + const printFormat = await frappe.db.get_value("Print Format", printDesignName, [ + "print_designer_header", + "print_designer_body", + "print_designer_after_table", + "print_designer_footer", + "print_designer_settings", + ]); + let settings = JSON.parse(printFormat.message.print_designer_settings); + this.loadSettings(settings); + + let ElementsBody = JSON.parse(printFormat.message.print_designer_body); + let ElementsAfterTable = JSON.parse(printFormat.message.print_designer_after_table); + const headers = JSON.parse(printFormat.message.print_designer_header); + const footers = JSON.parse(printFormat.message.print_designer_footer); + headers.forEach((header) => { + this.Headers.push(header); + }); + footers.forEach((footer) => { + this.Footers.push(footer); + }); + // backwards compatibility :( + if (ElementsAfterTable && ElementsAfterTable.length) { + ElementsBody[0].childrens.push(...ElementsAfterTable); + } + this.Elements.length = 0; + this.Elements.push(...ElementsBody); + ElementsBody.forEach((page, index) => { + page.header = [ + createHeaderFooterElement(this.getHeaderObject(index).childrens, "header"), + ]; + page.footer = [ + createHeaderFooterElement(this.getFooterObject(index).childrens, "footer"), + ]; + }); + this.Elements.forEach((page) => this.setElementProperties(page)); frappe.dom.unfreeze(); }, setPrimaryTable(tableEl, value) { @@ -1147,9 +1232,13 @@ export const useElementStore = defineStore("ElementStore", { }, // This is called to check if the element is overlapping with any other element (row only) // TODO: add column calculations - isElementOverlapping(currentEl, elements = this.Elements) { - const currentElIndex = - currentEl.index || this.Elements.findIndex((el) => el === currentEl); + isElementOverlapping(currentEl, elements = null) { + const MainStore = useMainStore(); + MainStore.activePage = getParentPage(currentEl.parent); + if (!elements) { + elements = MainStore.activePage.childrens; + } + const currentElIndex = currentEl.index || elements.findIndex((el) => el === currentEl); const currentStartY = parseInt(currentEl.startY); const currentEndY = parseInt(currentEl.startY + currentEl.height); diff --git a/print_designer/public/js/print_designer/store/MainStore.js b/print_designer/public/js/print_designer/store/MainStore.js index ac36cab..780f632 100644 --- a/print_designer/public/js/print_designer/store/MainStore.js +++ b/print_designer/public/js/print_designer/store/MainStore.js @@ -15,10 +15,12 @@ export const useMainStore = defineStore("MainStore", { */ textControlType: "dynamic", /** - * @type {'editing'|'pdfSetup'|'preview'} mode + * @type {'editing'|'footer'|'header'} mode */ schema_version: "1.2.0", mode: "editing", + activePage: null, + visiblePages: [], cursor: "url('/assets/print_designer/images/mouse-pointer.svg'), default !important", isMarqueeActive: false, isDrawing: false, @@ -41,7 +43,6 @@ export const useMainStore = defineStore("MainStore", { imageDocFields: [], snapPoints: [], snapEdges: [], - mainContainer: new Object(), propertiesContainer: new Object(), openModal: false, openDynamicModal: null, @@ -172,6 +173,16 @@ export const useMainStore = defineStore("MainStore", { return parseFloat(convertedUnit.value.toFixed(3)); }; }, + getPageStyle() { + switch (this.mode) { + case "editing": + return this.getPageSettings; + case "header": + return this.getHeaderSettings; + case "footer": + return this.getFooterSettings; + } + }, getPageSettings() { return { height: @@ -184,6 +195,24 @@ export const useMainStore = defineStore("MainStore", { ) + this.page.UOM, }; }, + getHeaderSettings() { + return { + height: this.convertToPageUOM(this.page.headerHeight) + this.page.UOM, + width: + this.convertToPageUOM( + this.page.width - this.page.marginLeft - this.page.marginRight + ) + this.page.UOM, + }; + }, + getFooterSettings() { + return { + height: this.convertToPageUOM(this.page.footerHeight) + this.page.UOM, + width: + this.convertToPageUOM( + this.page.width - this.page.marginLeft - this.page.marginRight + ) + this.page.UOM, + }; + }, getCurrentElementsValues() { return Object.values(this.currentElements); }, diff --git a/print_designer/public/js/print_designer/store/fetchMetaAndData.js b/print_designer/public/js/print_designer/store/fetchMetaAndData.js index f40b8f3..28778b8 100644 --- a/print_designer/public/js/print_designer/store/fetchMetaAndData.js +++ b/print_designer/public/js/print_designer/store/fetchMetaAndData.js @@ -131,60 +131,62 @@ export const fetchDoc = async (id = null) => { }, { immediate: true } ); - + const updateDynamicData = async () => { + MainStore.dynamicData.forEach(async (el) => { + if (el.is_static) return; + let value = el.parentField + ? await getValue(el.doctype, MainStore.docData[el.parentField], el.fieldname) + : el.tableName + ? MainStore.docData[el.tableName][0] && + frappe.format( + MainStore.docData[el.tableName][0][el.fieldname], + { fieldtype: el.fieldtype, options: el.options }, + { inline: true }, + MainStore.docData + ) + : frappe.format( + MainStore.docData[el.fieldname], + { fieldtype: el.fieldtype, options: el.options }, + { inline: true }, + MainStore.docData + ); + if (typeof value == "string" && value.startsWith("`)); + value = result[1]; + } + if (!value) { + if (["Image, Attach Image"].indexOf(el.fieldtype) != -1) { + value = null; + } else { + switch (el.fieldname) { + case "page": + value = "0"; + break; + case "topage": + value = "999"; + break; + case "date": + value = frappe.datetime.now_date(); + break; + case "time": + value = frappe.datetime.now_time(); + break; + default: + value = `{{ ${el.parentField ? el.parentField + "." : ""}${ + el.fieldname + } }}`; + } + } + } + el.value = value; + }); + }; watch( () => MainStore.docData, async () => { if (!Object.keys(MainStore.docData).length) return; await frappe.dom.freeze(); - MainStore.dynamicData.forEach(async (el) => { - if (el.is_static) return; - let value = el.parentField - ? await getValue(el.doctype, MainStore.docData[el.parentField], el.fieldname) - : el.tableName - ? MainStore.docData[el.tableName][0] && - frappe.format( - MainStore.docData[el.tableName][0][el.fieldname], - { fieldtype: el.fieldtype, options: el.options }, - { inline: true }, - MainStore.docData - ) - : frappe.format( - MainStore.docData[el.fieldname], - { fieldtype: el.fieldtype, options: el.options }, - { inline: true }, - MainStore.docData - ); - if (typeof value == "string" && value.startsWith("`)); - value = result[1]; - } - if (!value) { - if (["Image, Attach Image"].indexOf(el.fieldtype) != -1) { - value = null; - } else { - switch (el.fieldname) { - case "page": - value = "0"; - break; - case "topage": - value = "999"; - break; - case "date": - value = frappe.datetime.now_date(); - break; - case "time": - value = frappe.datetime.now_time(); - break; - default: - value = `{{ ${el.parentField ? el.parentField + "." : ""}${ - el.fieldname - } }}`; - } - } - } - el.value = value; - }); + await updateDynamicData(); await frappe.dom.unfreeze(); } ); diff --git a/print_designer/public/js/print_designer/utils.js b/print_designer/public/js/print_designer/utils.js index bf62e6b..6de214e 100644 --- a/print_designer/public/js/print_designer/utils.js +++ b/print_designer/public/js/print_designer/utils.js @@ -38,8 +38,12 @@ import { useDraggable } from "./composables/Draggable"; import { useResizable } from "./composables/Resizable"; import { useDropZone } from "./composables/DropZone"; import { isRef, nextTick } from "vue"; +import { getValue } from "./store/fetchMetaAndData"; export const changeDraggable = (element) => { + if (element.relativeContainer || element.DOMRef == null) { + return; + } if ( !element.isDraggable && interact.isSet(element.DOMRef) && @@ -53,7 +57,7 @@ export const changeDraggable = (element) => { ) { interact(element.DOMRef).draggable().enabled = true; } else if (element.isDraggable && !interact.isSet(element.DOMRef)) { - useDraggable(element.id); + useDraggable({ element }); } }; @@ -88,7 +92,7 @@ export const changeDropZone = (element) => { ) { interact(element.DOMRef).dropzone().enabled = true; } else if (element.isDropZone && !interact.isSet(element.DOMRef)) { - useDropZone(element.id); + useDropZone({ element }); } }; @@ -106,7 +110,7 @@ export const changeResizable = (element) => { ) { interact(element.DOMRef).resizable().enabled = true; } else if (element.isResizable && !interact.isSet(element.DOMRef)) { - useResizable(element.id); + useResizable({ element }); } }; @@ -203,17 +207,9 @@ const childrensCleanUp = (parentElement, element, isClone, isMainElement) => { } } if (isMainElement && isClone) { - if (Array.isArray(parentElement.parent)) { - parentElement.parent.push(element); - } else { - parentElement.parent.childrens.push(element); - } + parentElement.parent.childrens.push(element); } else if (!isMainElement) { - if (Array.isArray(parentElement)) { - parentElement.push(element); - } else { - parentElement.childrens.push(element); - } + parentElement.childrens.push(element); recursiveChildrens({ element, isClone, isMainElement: false }); } }; @@ -222,7 +218,10 @@ export const recursiveChildrens = ({ element, isClone = false, isMainElement = t const childrensArray = parentElement.childrens; isMainElement && childrensCleanUp(parentElement, element, isClone, isMainElement); parentElement.childrens = []; - if (parentElement.type == "rectangle" && childrensArray.length > 0) { + if ( + parentElement.type == "rectangle" || + (element.type == "page" && childrensArray.length > 0) + ) { childrensArray.forEach((element) => { childrensCleanUp(parentElement, element, isClone, false); }); @@ -280,9 +279,45 @@ export const updateElementParameters = (e) => { } }; +export const createHeaderFooterElement = (childrens, elementType) => { + const MainStore = useMainStore(); + const ElementStore = useElementStore(); + let id = frappe.utils.get_random(10); + const pageHeader = { + type: "rectangle", + childrens: [], + DOMRef: null, + id: id, + isDraggable: false, + isResizable: false, + isDropZone: true, + relativeContainer: true, + elementType: elementType, + startX: 0, + startY: + elementType == "header" + ? 0 + : MainStore.page.height - + MainStore.page.footerHeight - + MainStore.page.marginTop - + MainStore.page.marginBottom, + pageX: 0, + pageY: 0, + width: MainStore.page.width - MainStore.page.marginLeft - MainStore.page.marginRight, + height: + elementType == "header" ? MainStore.page.headerHeight : MainStore.page.footerHeight, + styleEditMode: "main", + style: { border: "none" }, + classes: [], + }; + pageHeader.childrens = childrens.map((el) => ElementStore.childrensSave(el)); + ElementStore.setElementProperties(pageHeader); + return { ...pageHeader }; +}; + export const deleteSnapObjects = (element, recursive = false) => { const MainStore = useMainStore(); - if (!element) { + if (!element || !element.snapPoints || !element.snapEdges) { return; } element.snapPoints.forEach((point) => { @@ -291,7 +326,10 @@ export const deleteSnapObjects = (element, recursive = false) => { element.snapEdges.forEach((point) => { MainStore.snapEdges.splice(MainStore.snapEdges.indexOf(point), 1); }); - if (recursive && element.type == "rectangle" && element.childrens.length > 0) { + if ( + (recursive && element.type == "rectangle") || + (element.type == "page" && element.childrens.length > 0) + ) { element.childrens.forEach((el) => { deleteSnapObjects(el, recursive); }); @@ -312,34 +350,71 @@ const deleteDynamicReferance = (curobj) => { }); } }; - +export const updateDynamicData = async () => { + const MainStore = useMainStore(); + if (!Object.keys(MainStore.docData).length) return; + MainStore.dynamicData.forEach(async (el) => { + if (el.is_static) return; + let value = el.parentField + ? await getValue(el.doctype, MainStore.docData[el.parentField], el.fieldname) + : el.tableName + ? MainStore.docData[el.tableName][0] && + frappe.format( + MainStore.docData[el.tableName][0][el.fieldname], + { fieldtype: el.fieldtype, options: el.options }, + { inline: true }, + MainStore.docData + ) + : frappe.format( + MainStore.docData[el.fieldname], + { fieldtype: el.fieldtype, options: el.options }, + { inline: true }, + MainStore.docData + ); + if (typeof value == "string" && value.startsWith("`)); + value = result[1]; + } + if (!value) { + if (["Image, Attach Image"].indexOf(el.fieldtype) != -1) { + value = null; + } else { + switch (el.fieldname) { + case "page": + value = "0"; + break; + case "topage": + value = "999"; + break; + case "date": + value = frappe.datetime.now_date(); + break; + case "time": + value = frappe.datetime.now_time(); + break; + default: + value = `{{ ${el.parentField ? el.parentField + "." : ""}${ + el.fieldname + } }}`; + } + } + } + el.value = value; + }); +}; export const deleteCurrentElements = () => { const MainStore = useMainStore(); const ElementStore = useElementStore(); if (MainStore.getCurrentElementsValues.length === 1) { let curobj = MainStore.getCurrentElementsValues[0]; deleteDynamicReferance(curobj); - if (Array.isArray(curobj.parent)) { - deleteSnapObjects(curobj.parent.splice(curobj.index, 1)[0], true); - } else { - deleteSnapObjects(curobj.parent.childrens.splice(curobj.index, 1)[0], true); - } + deleteSnapObjects(curobj.parent.childrens.splice(curobj.index, 1)[0], true); } else { MainStore.getCurrentElementsValues.forEach((element) => { - if (Array.isArray(element.parent)) { - deleteSnapObjects( - element.parent.splice(element.parent.indexOf(element), 1)[0], - true - ); - } else { - deleteSnapObjects( - element.parent.childrens.splice( - element.parent.childrens.indexOf(element), - 1 - )[0], - true - ); - } + deleteSnapObjects( + element.parent.childrens.splice(element.parent.childrens.indexOf(element), 1)[0], + true + ); }); } MainStore.lastCreatedElement = null; @@ -448,12 +523,11 @@ export const getSnapPointsAndEdges = (element) => { export const handleAlignIconClick = (value) => { const MainStore = useMainStore(); - const ElementStore = useElementStore(); let currentElements = MainStore.getCurrentElementsValues; let parent; MainStore.getCurrentElementsValues.forEach((element) => { if (parent == null) { - if (Array.isArray(element.parent)) { + if (element.parent.type == "page") { parent = false; return; } @@ -531,7 +605,9 @@ export const handleAlignIconClick = (value) => { break; } } else if (currentElements.length > 1) { - let parentRect = MainStore.mainContainer.getBoundingClientRect(); + let parentRect = getParentPage( + MainStore.getCurrentElementsValues[0].parent + ).DOMRef.getBoundingClientRect(); if (parent) { parentRect = parent.DOMRef.getBoundingClientRect(); } @@ -617,6 +693,28 @@ export const handleBorderIconClick = (element, icon) => { } }; +export const getParentPage = (element) => { + if (!element) return; + if (element.type == "page") { + return element; + } else { + if (element.parent) { + return getParentPage(element.parent); + } + return; + } +}; + +export const isInHeaderFooter = (element) => { + if (!element) return false; + if (!element.parent) return false; + if (element.elementType == "header") { + return true; + } else { + return isInHeaderFooter(element.parent); + } +}; + const getGlobalStyleObject = (object = null, checkProperty = null) => { const MainStore = useMainStore(); let globalStyleName = MainStore.activeControl; @@ -812,9 +910,9 @@ export const checkUpdateElementOverlapping = (element = null) => { const MainStore = useMainStore(); const ElementStore = useElementStore(); nextTick(() => { - if (element && !Array.isArray(element.parent)) return; + if (!element || element.parent.type != "page") return; isOlderSchema = MainStore.isOlderSchema("1.1.0"); - element.parent.forEach((el) => { + element.parent.childrens.forEach((el) => { const isElementOverlapping = ElementStore.isElementOverlapping(el); if (el.isElementOverlapping != isElementOverlapping) { el.isElementOverlapping = isElementOverlapping; From a60fbfa74b138a41ae0453d2dc543cddf54866ec Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Sat, 8 Jun 2024 11:13:38 +0530 Subject: [PATCH 05/15] fix: delete DOMRef and parent in header/footer element parent references causes circular import issue when we stringify with json. --- print_designer/public/js/print_designer/store/ElementStore.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/print_designer/public/js/print_designer/store/ElementStore.js b/print_designer/public/js/print_designer/store/ElementStore.js index 8761b86..05ffb3f 100644 --- a/print_designer/public/js/print_designer/store/ElementStore.js +++ b/print_designer/public/js/print_designer/store/ElementStore.js @@ -296,6 +296,8 @@ export const useElementStore = defineStore("ElementStore", { const footerArray = [...this.Footers]; headerArray.forEach((h) => { const headerCopy = { ...h }; + delete headerCopy.DOMRef; + delete headerCopy.parent; h.childrens = this.cleanUpElementsForSave(h.childrens, "header") || []; header.push(headerCopy); }); @@ -313,6 +315,8 @@ export const useElementStore = defineStore("ElementStore", { }); footerArray.forEach((f) => { const footerCopy = { ...f }; + delete footerCopy.DOMRef; + delete footerCopy.parent; footerCopy.childrens = this.cleanUpElementsForSave(f.childrens, "footer") || []; footer.push(footerCopy); }); From 11c91bfadaaa35329d61bdeac38cdb57b6cfa708 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 11 Jun 2024 16:22:40 +0530 Subject: [PATCH 06/15] fix: if heightType is not available check isDynamicHeight old schema had isDynamicHeight so if heightType is not available we can use isDynamicHeight --- .../page/print_designer/jinja/macros/rectangle.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/print_designer/print_designer/page/print_designer/jinja/macros/rectangle.html b/print_designer/print_designer/page/print_designer/jinja/macros/rectangle.html index e795ad7..52a6b63 100644 --- a/print_designer/print_designer/page/print_designer/jinja/macros/rectangle.html +++ b/print_designer/print_designer/page/print_designer/jinja/macros/rectangle.html @@ -1,9 +1,9 @@ {% macro rectangle(element, render_element, send_to_jinja, heightType) -%} {%- set heightType = element.get("heightType") -%} - {%- if settings.get("schema_version") == "1.1.0" -%} + {%- if settings.get("schema_version") == "1.1.0" or heightType == None -%} {%- set heightType = "auto" if element.get("isDynamicHeight", False) else "fixed" -%} {%- endif -%} -
{% if element.childrens %} {% for object in element.childrens %} From 82e4661832b39058aa71e3b23c5b9dbf434cb456 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 11 Jun 2024 16:23:40 +0530 Subject: [PATCH 07/15] chore: remove extra }} in spantag.html --- .../page/print_designer/jinja/macros/spantag.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/print_designer/print_designer/page/print_designer/jinja/macros/spantag.html b/print_designer/print_designer/page/print_designer/jinja/macros/spantag.html index 20e0e45..587c202 100644 --- a/print_designer/print_designer/page/print_designer/jinja/macros/spantag.html +++ b/print_designer/print_designer/page/print_designer/jinja/macros/spantag.html @@ -31,7 +31,7 @@ {{ _(field.label) }} {% endif %} - {{ span_value }} From 8e10c33e340737e1e16d4d682783ab0dd1468778 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 11 Jun 2024 16:24:04 +0530 Subject: [PATCH 08/15] fix: check if DOMRef exist before accessing there are times when DOM Reference is not available, so we need to check if it exists before accessing it --- .../js/print_designer/components/base/BaseDynamicText.vue | 1 + .../js/print_designer/components/base/BaseStaticText.vue | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/print_designer/public/js/print_designer/components/base/BaseDynamicText.vue b/print_designer/public/js/print_designer/components/base/BaseDynamicText.vue index f45e486..b7e036f 100644 --- a/print_designer/public/js/print_designer/components/base/BaseDynamicText.vue +++ b/print_designer/public/js/print_designer/components/base/BaseDynamicText.vue @@ -232,6 +232,7 @@ const handleDblClick = (e, element) => { onMounted(() => { selectedDynamicText.value = null; + if (!DOMRef || !!DOMRef.value.firstElementChild.innerText) return; nextTick(() => { DOMRef.value.firstElementChild.dataset.placeholder = "Choose Dynamic Field..."; }); diff --git a/print_designer/public/js/print_designer/components/base/BaseStaticText.vue b/print_designer/public/js/print_designer/components/base/BaseStaticText.vue index cd28098..532c9eb 100644 --- a/print_designer/public/js/print_designer/components/base/BaseStaticText.vue +++ b/print_designer/public/js/print_designer/components/base/BaseStaticText.vue @@ -195,14 +195,14 @@ const handleDblClick = (e, element, index) => { }; onMounted(() => { - if (!!DOMRef.value.firstElementChild.innerText) return; + if (!DOMRef || !!DOMRef.value.firstElementChild.innerText) return; setTimeout(function () { DOMRef.value.firstElementChild.focus(); DOMRef.value.firstElementChild.dataset.placeholder = "Type Something..."; }, 0); }); onUpdated(() => { - if (!isFixedSize.value) { + if (!isFixedSize.value && DOMRef) { let targetRect = DOMRef.value.getBoundingClientRect(); width.value = targetRect.width + 2; height.value = targetRect.height + 2; From c4faa38b824c6e390afe6ef1c7aab7c16f64126d Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 11 Jun 2024 16:25:05 +0530 Subject: [PATCH 09/15] fix: evaluate condition for element type in realtime previously, we set the property in element but it is better to just check it in realtime. --- .../public/js/print_designer/PropertiesPanelState.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/print_designer/public/js/print_designer/PropertiesPanelState.js b/print_designer/public/js/print_designer/PropertiesPanelState.js index 429b5fa..977dcf3 100644 --- a/print_designer/public/js/print_designer/PropertiesPanelState.js +++ b/print_designer/public/js/print_designer/PropertiesPanelState.js @@ -7,6 +7,7 @@ import { handleAlignIconClick, handleBorderIconClick, getConditonalObject, + getParentPage, } from "./utils"; export const createPropertiesPanel = () => { const MainStore = useMainStore(); @@ -574,7 +575,12 @@ export const createPropertiesPanel = () => { labelDirection: "column", condtional: () => { const currentEl = MainStore.getCurrentElementsValues[0]; - if (currentEl.isElementOverlapping) { + if ( + ElementStore.isElementOverlapping( + currentEl, + getParentPage(currentEl).childrens + ) + ) { return false; } if ( From fce193988225bc651c17f675f0e4c90ee1a5d6a5 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 11 Jun 2024 16:32:08 +0530 Subject: [PATCH 10/15] fix: dynamic heights for overlapping elements - fix: commented out debugging code - fix: footer element positional values were incorrect - fix: overlapping elements were causing layout breaks - updated elementOverlapping code to only allow consider if element is directly overlapping (x,y axis) another element --- .../js/print_designer/store/ElementStore.js | 90 +++++++++++++------ 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/print_designer/public/js/print_designer/store/ElementStore.js b/print_designer/public/js/print_designer/store/ElementStore.js index 05ffb3f..321f5f8 100644 --- a/print_designer/public/js/print_designer/store/ElementStore.js +++ b/print_designer/public/js/print_designer/store/ElementStore.js @@ -96,9 +96,9 @@ export const useElementStore = defineStore("ElementStore", { // this.Elements.length = 0; // this.Headers.length = 0; // this.Footers.length = 0; - // this.Headers.push(...layout.header); - // this.Elements.push(...layout.body); - // this.Footers.push(...layout.footer); + // this.Headers.push(...header); + // this.Elements.push(...body); + // this.Footers.push(...footer); // this.Elements.forEach((page, index) => { // page.header = [createHeaderFooterElement(this.getHeaderObject(index).childrens, "header")]; // page.footer = [createHeaderFooterElement(this.getFooterObject(index).childrens, "footer")] @@ -389,12 +389,8 @@ export const useElementStore = defineStore("ElementStore", { lastHeaderRow.height = MainStore.page.headerHeight - MainStore.page.marginTop - lastHeaderRow.startY; } else if (type == "footer" && rowElements.length) { - const lastHeaderRow = rowElements[rowElements.length - 1]; - lastHeaderRow.height = - MainStore.page.height - - MainStore.page.marginTop - - MainStore.page.marginBottom - - lastHeaderRow.startY; + const lastFooterRow = rowElements[rowElements.length - 1]; + lastFooterRow.height = MainStore.page.footerHeight - lastFooterRow.startY; } return rowElements; }, @@ -619,12 +615,6 @@ export const useElementStore = defineStore("ElementStore", { offsetRect.top = MainStore.page.headerHeight; } } - if (containerType == "footer" && index == 0) { - offsetRect.top = - MainStore.page.height - - MainStore.page.footerHeightWithMargin - - MainStore.page.marginTop; - } // element is parent level row. if (index > 0 && ["header", "body", "footer"].includes(containerType)) { offsetRect.top = prevDimensions.bottom; @@ -804,9 +794,22 @@ export const useElementStore = defineStore("ElementStore", { }, updateChildrenInRowWrapper(wrapper, children) { wrapper.childrens = children; - if (wrapper.childrens.some((el) => el.heightType == "auto-min-height")) { + if ( + (wrapper.childrens.length == 1 && + wrapper.childrens[0].heightType == "auto-min-height") || + wrapper.childrens.some( + (el) => + ["row", "column"].includes(el.layoutType) && + el.heightType == "auto-min-height" + ) + ) { wrapper.heightType = "auto-min-height"; - } else if (wrapper.childrens.some((el) => el.heightType == "auto")) { + } else if ( + (wrapper.childrens.length == 1 && wrapper.childrens[0].heightType == "auto") || + wrapper.childrens.some( + (el) => ["row", "column"].includes(el.layoutType) && el.heightType == "auto" + ) + ) { wrapper.heightType = "auto"; } else { wrapper.heightType = "fixed"; @@ -893,9 +896,21 @@ export const useElementStore = defineStore("ElementStore", { this.computeLayoutInsideRectangle(childElements); } this.updateChildrenInRowWrapper(wrapper, childElements); - if (childElements.some((el) => el.heightType == "auto-min-height")) { + if ( + (childElements.length == 1 && childElements[0].heightType == "auto-min-height") || + childElements.some( + (el) => + ["row", "column"].includes(el.layoutType) && + el.heightType == "auto-min-height" + ) + ) { wrapper.heightType = "auto-min-height"; - } else if (childElements.some((el) => el.heightType == "auto")) { + } else if ( + (childElements.length == 1 && childElements[0].heightType == "auto") || + childElements.some( + (el) => ["row", "column"].includes(el.layoutType) && el.heightType == "auto" + ) + ) { wrapper.heightType = "auto"; } else { wrapper.heightType = "fixed"; @@ -929,9 +944,21 @@ export const useElementStore = defineStore("ElementStore", { this.computeLayoutInsideRectangle(childElements); } this.updateChildrenInColumnWrapper(wrapper, childElements); - if (childElements.some((el) => el.heightType == "auto-min-height")) { + if ( + (childElements.length == 1 && childElements[0].heightType == "auto-min-height") || + childElements.some( + (el) => + ["row", "column"].includes(el.layoutType) && + el.heightType == "auto-min-height" + ) + ) { wrapper.heightType = "auto-min-height"; - } else if (childElements.some((el) => el.heightType == "auto")) { + } else if ( + (childElements.length == 1 && childElements[0].heightType == "auto") || + childElements.some( + (el) => ["row", "column"].includes(el.layoutType) && el.heightType == "auto" + ) + ) { wrapper.heightType = "auto"; } else { wrapper.heightType = "fixed"; @@ -1244,30 +1271,43 @@ export const useElementStore = defineStore("ElementStore", { } const currentElIndex = currentEl.index || elements.findIndex((el) => el === currentEl); const currentStartY = parseInt(currentEl.startY); + const currentStartX = parseInt(currentEl.startX); const currentEndY = parseInt(currentEl.startY + currentEl.height); - + const currentEndX = parseInt(currentEl.startX + currentEl.width); return ( elements.findIndex((el, index) => { if (index == currentElIndex) return false; const elStartY = parseInt(el.startY); const elEndY = parseInt(el.startY + el.height); + const elStartX = parseInt(el.startX); + const elEndX = parseInt(el.startX + el.width); if ( currentStartY <= elStartY && elStartY <= currentEndY && - !(currentStartY <= elEndY && elEndY <= currentEndY) + !(currentStartY <= elEndY && elEndY <= currentEndY) && + currentStartX <= elStartX && + elStartX <= currentEndX && + !(currentStartX <= elEndX && elEndX <= currentEndX) ) { return true; } else if ( !(currentStartY <= elStartY && elStartY <= currentEndY) && currentStartY <= elEndY && - elEndY <= currentEndY + elEndY <= currentEndY && + !(currentStartX <= elStartX && elStartX <= currentEndX) && + currentStartX <= elEndX && + elEndX <= currentEndX ) { return true; } else if ( elStartY <= currentStartY && currentStartY <= elEndY && elStartY <= currentEndY && - currentEndY <= elEndY + currentEndY <= elEndY && + elStartX <= currentStartX && + currentStartX <= elEndX && + elStartX <= currentEndX && + currentEndX <= elEndX ) { return true; } else { From daff1dc485d5161bbfc5c6f2154c15e2e1c954c0 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 11 Jun 2024 16:42:03 +0530 Subject: [PATCH 11/15] chore: confirm before deleting page --- .../js/print_designer/PropertiesPanelState.js | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/print_designer/public/js/print_designer/PropertiesPanelState.js b/print_designer/public/js/print_designer/PropertiesPanelState.js index 977dcf3..f631fd7 100644 --- a/print_designer/public/js/print_designer/PropertiesPanelState.js +++ b/print_designer/public/js/print_designer/PropertiesPanelState.js @@ -412,10 +412,34 @@ export const createPropertiesPanel = () => { style: "secondary", margin: 15, onClick: (e, field) => { - ElementStore.Elements.splice( - ElementStore.Elements.indexOf(MainStore.activePage), - 1 - ); + if (MainStore.activePage?.childrens.length) { + let message = __("Are you sure you want to delete the page?"); + frappe.confirm(message, () => { + ElementStore.Elements.splice( + ElementStore.Elements.indexOf(MainStore.activePage), + 1 + ); + frappe.show_alert( + { + message: `Page Deleted Successfully`, + indicator: "green", + }, + 5 + ); + }); + } else { + ElementStore.Elements.splice( + ElementStore.Elements.indexOf(MainStore.activePage), + 1 + ); + frappe.show_alert( + { + message: `Page Deleted Successfully`, + indicator: "green", + }, + 5 + ); + } e.target.blur(); }, }, From 2363ad9c6567ab032c0c311d52ddea89ec214f2c Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 11 Jun 2024 17:32:58 +0530 Subject: [PATCH 12/15] fix: patch update print_designer_print_format - update print_designer_print_format to use the new object syntax --- .../move_header_footers_to_new_schema.py | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/print_designer/patches/move_header_footers_to_new_schema.py b/print_designer/patches/move_header_footers_to_new_schema.py index 0eb9e67..143e5a8 100644 --- a/print_designer/patches/move_header_footers_to_new_schema.py +++ b/print_designer/patches/move_header_footers_to_new_schema.py @@ -1,5 +1,7 @@ import frappe +from print_designer.pdf import is_older_schema + def patch_format(): print_formats = frappe.get_all( @@ -57,16 +59,34 @@ def patch_format(): "isDropZone": True, } ] + object_to_save = { + "print_designer_header": frappe.json.dumps(header_data), + "print_designer_body": frappe.json.dumps(bodyPage), + "print_designer_footer": frappe.json.dumps(footer_data), + "print_designer_settings": frappe.json.dumps(settings), + } + if not is_older_schema(settings=settings, current_version="1.1.0"): + pf_print_format = frappe.json.loads(pf.print_designer_print_format) + if "header" in pf_print_format: + pf_print_format["header"] = { + "firstPage": pf_print_format["header"], + "oddPage": pf_print_format["header"], + "evenPage": pf_print_format["header"], + "lastPage": pf_print_format["header"], + } + if "footer" in pf_print_format: + pf_print_format["footer"] = { + "firstPage": pf_print_format["footer"], + "oddPage": pf_print_format["footer"], + "evenPage": pf_print_format["footer"], + "lastPage": pf_print_format["footer"], + } + object_to_save["print_designer_print_format"] = frappe.json.dumps(pf_print_format) frappe.set_value( "Print Format", pf.name, - { - "print_designer_header": frappe.json.dumps(header_data), - "print_designer_body": frappe.json.dumps(bodyPage), - "print_designer_footer": frappe.json.dumps(footer_data), - "print_designer_settings": frappe.json.dumps(settings), - }, + object_to_save, ) return print_formats From 6fc0d13105a51904ca5a88a4c051423f8000e3fc Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 11 Jun 2024 17:47:52 +0530 Subject: [PATCH 13/15] chore: make copy of format on save. it still have some breaking issues so it's better to have a copy of the original format. --- print_designer/public/js/print_designer/store/ElementStore.js | 2 +- print_designer/public/js/print_designer/store/MainStore.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/print_designer/public/js/print_designer/store/ElementStore.js b/print_designer/public/js/print_designer/store/ElementStore.js index 321f5f8..908629e 100644 --- a/print_designer/public/js/print_designer/store/ElementStore.js +++ b/print_designer/public/js/print_designer/store/ElementStore.js @@ -257,7 +257,7 @@ export const useElementStore = defineStore("ElementStore", { objectToSave.print_designer_settings = JSON.stringify(settingsForSave); objectToSave.print_designer_after_table = null; objectToSave.css = css; - if (MainStore.isOlderSchema("1.1.0")) { + if (MainStore.isOlderSchema("1.3.0")) { await this.printFormatCopyOnOlderSchema(objectToSave); } else { await frappe.db.set_value("Print Format", MainStore.printDesignName, objectToSave); diff --git a/print_designer/public/js/print_designer/store/MainStore.js b/print_designer/public/js/print_designer/store/MainStore.js index 780f632..23c1275 100644 --- a/print_designer/public/js/print_designer/store/MainStore.js +++ b/print_designer/public/js/print_designer/store/MainStore.js @@ -17,7 +17,7 @@ export const useMainStore = defineStore("MainStore", { /** * @type {'editing'|'footer'|'header'} mode */ - schema_version: "1.2.0", + schema_version: "1.3.0", mode: "editing", activePage: null, visiblePages: [], From b9ac00dd11f74e29bcd8aa6d1eb80b4963a0e32b Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 11 Jun 2024 18:00:21 +0530 Subject: [PATCH 14/15] Revert "chore: better structure for header/footer doctype" This reverts commit 1d5caf92f35aaddf8a1ece840ec10a7eb16c7f02. --- .../print_designer/doctype/__init__.py | 0 .../pd_header_footer/pd_header_footer.js | 17 ++------ .../pd_header_footer/pd_header_footer.json | 39 +++++++------------ 3 files changed, 17 insertions(+), 39 deletions(-) delete mode 100644 print_designer/print_designer/doctype/__init__.py diff --git a/print_designer/print_designer/doctype/__init__.py b/print_designer/print_designer/doctype/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js index 0500ec7..3d8ec21 100644 --- a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js +++ b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js @@ -1,17 +1,8 @@ // Copyright (c) 2024, Frappe Technologies Pvt Ltd. and contributors // For license information, please see license.txt -const set_template_app_options = (frm) => { - frappe.xcall("frappe.core.doctype.module_def.module_def.get_installed_apps").then((r) => { - frm.set_df_property("export_template_app", "options", JSON.parse(r)); - if (!frm.doc.export_template_app) { - frm.set_value("export_template_app", "print_designer"); - } - }); -}; +// frappe.ui.form.on("PD Header Footer", { +// refresh(frm) { -frappe.ui.form.on("PD Header Footer", { - refresh(frm) { - set_template_app_options(frm); - }, -}); +// }, +// }); diff --git a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json index 7a1fbcf..3fb387a 100644 --- a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json +++ b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json @@ -7,15 +7,23 @@ "field_order": [ "section_break_otix", "title", - "export_template_app", "column_break_lcet", "print_format", - "type", "section_break_codz", - "data", - "settings" + "header", + "footer" ], "fields": [ + { + "fieldname": "header", + "fieldtype": "JSON", + "label": "Header" + }, + { + "fieldname": "footer", + "fieldtype": "JSON", + "label": "Footer" + }, { "fieldname": "section_break_otix", "fieldtype": "Section Break", @@ -43,32 +51,11 @@ "label": "Title", "length": 100, "unique": 1 - }, - { - "fieldname": "type", - "fieldtype": "Select", - "label": "Type", - "options": "Header\nFooter" - }, - { - "fieldname": "export_template_app", - "fieldtype": "Select", - "label": "Export Template App" - }, - { - "fieldname": "data", - "fieldtype": "JSON", - "label": "Data" - }, - { - "fieldname": "settings", - "fieldtype": "JSON", - "label": "Settings" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-06-02 13:41:44.173915", + "modified": "2024-06-02 13:16:05.140853", "modified_by": "Administrator", "module": "Print Designer", "name": "PD Header Footer", From f3cada722486957b83f75a10c665b980213af9da Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Tue, 11 Jun 2024 18:00:26 +0530 Subject: [PATCH 15/15] Revert "feat: Header / Footer Doctype" This reverts commit c2121e818fb17b6388fc071188ddd9332ac1524f. --- .../doctype/pd_header_footer/__init__.py | 0 .../pd_header_footer/pd_header_footer.js | 8 -- .../pd_header_footer/pd_header_footer.json | 81 ------------------- .../pd_header_footer/pd_header_footer.py | 9 --- .../pd_header_footer/test_pd_header_footer.py | 9 --- 5 files changed, 107 deletions(-) delete mode 100644 print_designer/print_designer/doctype/pd_header_footer/__init__.py delete mode 100644 print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js delete mode 100644 print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json delete mode 100644 print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.py delete mode 100644 print_designer/print_designer/doctype/pd_header_footer/test_pd_header_footer.py diff --git a/print_designer/print_designer/doctype/pd_header_footer/__init__.py b/print_designer/print_designer/doctype/pd_header_footer/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js deleted file mode 100644 index 3d8ec21..0000000 --- a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2024, Frappe Technologies Pvt Ltd. and contributors -// For license information, please see license.txt - -// frappe.ui.form.on("PD Header Footer", { -// refresh(frm) { - -// }, -// }); diff --git a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json deleted file mode 100644 index 3fb387a..0000000 --- a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "actions": [], - "autoname": "field:title", - "creation": "2024-06-02 13:05:19.762196", - "doctype": "DocType", - "engine": "InnoDB", - "field_order": [ - "section_break_otix", - "title", - "column_break_lcet", - "print_format", - "section_break_codz", - "header", - "footer" - ], - "fields": [ - { - "fieldname": "header", - "fieldtype": "JSON", - "label": "Header" - }, - { - "fieldname": "footer", - "fieldtype": "JSON", - "label": "Footer" - }, - { - "fieldname": "section_break_otix", - "fieldtype": "Section Break", - "options": "Print Format" - }, - { - "fieldname": "column_break_lcet", - "fieldtype": "Column Break" - }, - { - "fieldname": "print_format", - "fieldtype": "Link", - "label": "Print Format", - "link_filters": "[[\"Print Format\",\"print_designer\",\"=\",1]]", - "options": "Print Format", - "read_only": 1 - }, - { - "fieldname": "section_break_codz", - "fieldtype": "Section Break" - }, - { - "fieldname": "title", - "fieldtype": "Data", - "label": "Title", - "length": 100, - "unique": 1 - } - ], - "index_web_pages_for_search": 1, - "links": [], - "modified": "2024-06-02 13:16:05.140853", - "modified_by": "Administrator", - "module": "Print Designer", - "name": "PD Header Footer", - "naming_rule": "By fieldname", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "sort_field": "creation", - "sort_order": "DESC", - "states": [] -} \ No newline at end of file diff --git a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.py b/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.py deleted file mode 100644 index 0b588aa..0000000 --- a/print_designer/print_designer/doctype/pd_header_footer/pd_header_footer.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2024, Frappe Technologies Pvt Ltd. and contributors -# For license information, please see license.txt - -# import frappe -from frappe.model.document import Document - - -class PDHeaderFooter(Document): - pass diff --git a/print_designer/print_designer/doctype/pd_header_footer/test_pd_header_footer.py b/print_designer/print_designer/doctype/pd_header_footer/test_pd_header_footer.py deleted file mode 100644 index ae3bf3c..0000000 --- a/print_designer/print_designer/doctype/pd_header_footer/test_pd_header_footer.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2024, Frappe Technologies Pvt Ltd. and Contributors -# See license.txt - -# import frappe -from frappe.tests.utils import FrappeTestCase - - -class TestPDHeaderFooter(FrappeTestCase): - pass