UNPKG

reportbro-designer

Version:

Designer to create pdf and excel report layouts. The reports can be generated with reportbro-lib (a Python package) on the server.

506 lines (471 loc) 21.8 kB
import AddDeleteDocElementCmd from '../commands/AddDeleteDocElementCmd'; import AddDeleteParameterCmd from '../commands/AddDeleteParameterCmd'; import AddDeleteStyleCmd from '../commands/AddDeleteStyleCmd'; import CommandGroupCmd from '../commands/CommandGroupCmd'; import MovePanelItemCmd from '../commands/MovePanelItemCmd'; import SetValueCmd from '../commands/SetValueCmd'; import Parameter from '../data/Parameter'; import Style from '../data/Style'; import DocElement from '../elements/DocElement'; import * as utils from '../utils'; /** * A main panel item either represents a data object (doc element, parameter, etc.) or * a container (e.g. page header) for other panel items. * @class */ export default class MainPanelItem { constructor(panelName, parent, data, properties, rb) { this.properties = { hasChildren: false, showAdd: false, showDelete: true, hasDetails: true, visible: true, draggable: false, static: false, }; Object.assign(this.properties, properties); this.panelName = panelName; const name = (data !== null) ? data.getName() : (properties.name ? properties.name : ''); this.id = (data !== null) ? data.getId() : properties.id; this.parent = parent; this.data = data; this.rb = rb; this.children = []; this.dragEnterCount = 0; this.element = utils.createElement('li'); if (!this.properties.visible) { this.element.classList.add('rbroHidden'); } const itemDiv = utils.createElement('div', { id: `rbro_menu_item${this.id}`, class: 'rbroMenuItem' }); if (this.properties.draggable) { itemDiv.setAttribute('draggable', 'true'); itemDiv.addEventListener('dragstart', (event) => { event.dataTransfer.setData('text/plain', ''); // without setData dragging does not work in FF event.dataTransfer.effectAllowed = 'move'; this.rb.startBrowserDrag('panelItem', null, this.id); // avoid calling dragstart handler for main div which disables dragging for all other elements event.stopPropagation(); }); } itemDiv.addEventListener('dragover', (event) => { if (this.rb.isBrowserDragActive('panelItem') && this.rb.getBrowserDragId() !== this.id) { let dropInfo = this.getDropObjectInfo(); if (dropInfo.allowDrop) { // without preventDefault for dragover event, the drop event is not fired event.preventDefault(); event.stopPropagation(); } } }); itemDiv.addEventListener('dragenter', (event) => { if (this.rb.isBrowserDragActive('panelItem') && this.rb.getBrowserDragId() !== this.id) { let dropInfo = this.getDropObjectInfo(); if (dropInfo.allowDrop) { itemDiv.classList.add('rbroMenuItemDragOver'); this.dragEnterCount++; event.preventDefault(); // needed for IE } } }); itemDiv.addEventListener('dragleave', (event) => { if (this.rb.isBrowserDragActive('panelItem') && this.rb.getBrowserDragId() !== this.id) { let dropInfo = this.getDropObjectInfo(); if (dropInfo.allowDrop) { this.dragEnterCount--; if (this.dragEnterCount === 0) { itemDiv.classList.remove('rbroMenuItemDragOver'); } } } }); itemDiv.addEventListener('drop', (event) => { if (this.rb.isBrowserDragActive('panelItem') && this.rb.getBrowserDragId() !== this.id) { let dropInfo = this.getDropObjectInfo(); if (dropInfo.allowDrop) { this.dragEnterCount--; itemDiv.classList.remove('rbroMenuItemDragOver'); let cmdGroup = new CommandGroupCmd('Move panel item', this.rb); let draggedObj = this.rb.getDataObject(this.rb.getBrowserDragId()); if (draggedObj instanceof DocElement && draggedObj.getValue('containerId') !== dropInfo.container.getId()) { draggedObj.updatePositionAndSize(draggedObj.getValue('xVal'), draggedObj.getValue('yVal'), draggedObj.getValue('widthVal'), draggedObj.getValue('heightVal'), dropInfo.container.getSize(), cmdGroup); let cmd = new SetValueCmd( draggedObj.getId(), 'containerId', dropInfo.container.getId(), SetValueCmd.type.internal, this.rb); cmdGroup.addCommand(cmd); } let cmd = new MovePanelItemCmd( draggedObj.getPanelItem(), dropInfo.panel, dropInfo.position, this.rb); cmdGroup.addCommand(cmd); this.rb.executeCommand(cmdGroup); event.preventDefault(); return false; } } }); const nameDiv = utils.createElement('div', { class: 'rbroMenuItemText' }); nameDiv.append(utils.createElement('span', { id: `rbro_menu_item_name${this.id}` }, name)); if (this.properties.showAdd) { const elAddButton = utils.createElement( 'div', { id: `rbro_menu_item_add${this.id}`, class: 'rbroButton rbroRoundButton rbroIcon-plus' }); elAddButton.addEventListener('click', (event) => { if (panelName === 'parameter') { let cmd = new AddDeleteParameterCmd(true, {}, this.rb.getUniqueId(), this.getId(), -1, this.rb); this.rb.executeCommand(cmd); } else if (panelName === 'style') { let cmd = new AddDeleteStyleCmd(true, {}, this.rb.getUniqueId(), this.getId(), -1, this.rb); this.rb.executeCommand(cmd); } else if (panelName === 'watermarkText' || panelName === 'watermarkImage') { const initialData = { containerId: this.getId() }; let docElementType; if (panelName === 'watermarkText') { docElementType = DocElement.type.watermarkText; } else { docElementType = DocElement.type.watermarkImage; } const cmd = new AddDeleteDocElementCmd( true, docElementType, initialData, this.rb.getUniqueId(), this.getId(), -1, this.rb); this.rb.executeCommand(cmd); } let newItem = this.children[this.children.length - 1]; this.rb.selectObject(newItem.getId(), true); event.stopPropagation(); }); itemDiv.append(elAddButton); } if (this.properties.showDelete) { const elDeleteButton = utils.createElement('div', { class: 'rbroButton rbroDeleteButton rbroIcon-cancel' }); elDeleteButton.addEventListener('click', (event) => { let cmd = null; if (panelName === 'parameter') { cmd = new AddDeleteParameterCmd( false, this.getData().toJS(), this.getId(), this.parent.getId(), this.getSiblingPosition(), this.rb); } else if (panelName === 'style') { cmd = new CommandGroupCmd('Delete', this); this.getData().addCommandsForDelete(cmd); } else if (this.isDocElementPanel()) { if (this.getData() instanceof DocElement) { cmd = new CommandGroupCmd('Delete', this); this.getData().addCommandsForDelete(cmd); } } if (cmd !== null) { this.rb.executeCommand(cmd); } event.stopPropagation(); }); itemDiv.append(elDeleteButton); } itemDiv.addEventListener('click', (event) => { // only allow toggle children list of menu item if there are no details or menu item is currently selected if (!this.properties.hasDetails || document.getElementById(`rbro_menu_item${this.id}`).classList.contains('rbroMenuItemActive')) { let elChildren = document.getElementById(`rbro_menu_item_children${this.id}`); if (elChildren) { itemDiv.classList.toggle('rbroMenuItemOpen'); elChildren.classList.toggle('rbroHidden'); } } if (this.properties.hasDetails) { if (!this.rb.isSelectedObject(this.id)) { let clearSelection = true; if (this.isDocElementPanel()) { clearSelection = !event.shiftKey; } this.rb.selectObject(this.id, clearSelection); } else { if (event.shiftKey) { this.rb.deselectObject(this.id); } } } }); if (this.properties.hasChildren) { itemDiv.classList.add('rbroMenuItemNoChildren'); nameDiv.append( utils.createElement( 'div', { id: `rbro_menu_item_children_toggle${this.id}`, class: 'rbroMenuArrow rbroIcon-arrow-right' })); this.element.append( utils.createElement('ul', { id: `rbro_menu_item_children${this.id}`, class: 'rbroHidden' })); } itemDiv.prepend(nameDiv); this.element.prepend(itemDiv); } getId() { return this.id; } getElement() { return this.element; } show() { this.element.classList.remove('rbroHidden'); } hide() { this.element.classList.add('rbroHidden'); } getPanelName() { return this.panelName; } getParent() { return this.parent; } getData() { return this.data; } setData(data) { this.data = data; document.getElementById(`rbro_menu_item_name${this.id}`).textContent = (data !== null) ? data.getName() : ''; } setActive() { document.getElementById(`rbro_menu_item${this.id}`).classList.add('rbroMenuItemActive'); } setInactive() { document.getElementById(`rbro_menu_item${this.id}`).classList.remove('rbroMenuItemActive'); } getParentIds() { let ids = []; let parent = this.getParent(); while (parent !== null) { ids.push(parent.id); parent = parent.getParent(); } return ids; } openParentItems() { let parent = this.getParent(); while (parent !== null) { parent.open(); parent = parent.getParent(); } } open() { let elChildren = document.getElementById(`rbro_menu_item_children${this.getId()}`); if (elChildren) { document.getElementById(`rbro_menu_item${this.getId()}`).classList.add('rbroMenuItemOpen'); elChildren.classList.remove('rbroHidden'); } } close() { let elChildren = document.getElementById(`rbro_menu_item_children${this.getId()}`); if (elChildren) { document.getElementById(`rbro_menu_item${this.getId()}`).classList.remove('rbroMenuItemOpen'); elChildren.classList.add('rbroHidden'); } } appendChild(child) { if (this.children.length === 0) { document.getElementById(`rbro_menu_item${this.getId()}`).classList.remove('rbroMenuItemNoChildren'); } this.children.push(child); document.getElementById(`rbro_menu_item_children${this.getId()}`).append(child.getElement()); } insertChild(pos, child) { if (this.children.length === 0) { document.getElementById(`rbro_menu_item${this.getId()}`).classList.remove('rbroMenuItemNoChildren'); } if (pos !== -1) { this.children.splice(pos, 0, child); } else { this.children.push(child); } let elChildren = document.querySelectorAll(`#rbro_menu_item_children${this.getId()} > li`); if (pos >= 0 && pos < elChildren.length) { elChildren[pos].before(child.getElement()); } else { document.getElementById(`rbro_menu_item_children${this.getId()}`).append(child.getElement()); } } getChildren() { return this.children; } /** * Returns child where its data object matches the given name. * * If multiple children have the same name the first child is returned. * * @param {string} name - name of child to search for. * @returns {?MainPanelItem} child panel or null if no child with given name exists. */ getChildByName(name) { return this.getChildByNameExclude(name, null); } /** * Returns child where its data object matches the given name but only if not explicitly excluded. * * If multiple children have the same name the first child is returned. * * @param {string} name - name of child to search for. * @param {?Object} excludeChild - data object which will be excluded from search. * @returns {?MainPanelItem} child panel or null if no child with given name exists. */ getChildByNameExclude(name, excludeChild) { for (let child of this.children) { if (child.getData() !== null && child.getData() !== excludeChild && child.getData().getName() === name) { return child; } } return null; } removeChild(child) { this.removeChildInternal(child, true); } removeChildInternal(child, deleteDomNode) { for (let i=0; i < this.children.length; i++) { if (child.getId() === this.children[i].getId()) { this.children.splice(i, 1); if (deleteDomNode) { child.getElement().remove(); } if (this.children.length === 0) { const elMenuItem = document.getElementById(`rbro_menu_item${this.getId()}`); // test if menu item exists, when all document elements are deleted before // loading a report and the parent element of this child was deleted before (e.g. parent is // a frame element and the child a text element inside the frame) then the menu item does // not exist anymore when deleting the child. if (elMenuItem !== null) { elMenuItem.classList.add('rbroMenuItemNoChildren'); } } break; } } } getSiblingPosition() { if (this.getParent() !== null) { let siblings = this.getParent().getChildren(); for (let i=0; i < siblings.length; i++) { if (siblings[i] === this) { return i; } } } return 0; } /** * Move panel item to another parent. * The panel will be appended to the parent, i.e. added after all children of the parent. * @param {MainPanelItem} parentPanelItem - new parent panel */ moveTo(parentPanelItem) { this.element.parentElement.removeChild(this.element); this.parent.removeChildInternal(this, false); this.parent = parentPanelItem; parentPanelItem.appendChild(this); } /** * Move panel item to another parent at given position. * @param {MainPanelItem} parentPanelItem - new parent panel * @param {Number} pos - Position index in children list of new parent where the panel will be inserted. */ moveToPosition(parentPanelItem, pos) { this.element.parentElement.removeChild(this.element); this.parent.removeChildInternal(this, false); this.parent = parentPanelItem; parentPanelItem.insertChild(pos, this); } clear() { if (this.children.length > 0) { // static items (e.g. sub category for watermarks) are not cleared, therefor the children have // to be cleared individually if (this.properties.static) { for (const child of this.children) { child.clear(); } } else { utils.emptyElement(document.getElementById(`rbro_menu_item_children${this.id}`)); this.children = []; } } } getDropObjectInfo() { let rv = { allowDrop: false, panel: null, position: -1, container: null }; let draggedObj = this.rb.getDataObject(this.rb.getBrowserDragId()); if (draggedObj !== null) { let dropIntoParent = false; if (draggedObj instanceof DocElement) { if (this.data instanceof DocElement && this.data.isDroppingAllowed()) { // get linked container if available (e.g. container of frame element), // otherwise use the parent container rv.container = this.data.getLinkedContainer(); if (rv.container === null) { rv.container = this.data.getContainer(); dropIntoParent = true; } } else if (this.panelName === 'band') { rv.container = this.data; } if (rv.container !== null && rv.container.isElementAllowed(draggedObj.getElementType())) { rv.allowDrop = true; // if the dragged object has linked containers (section or frame) then we make sure // we cannot drag the object into one of its container children for (const linkedContainer of draggedObj.getLinkedContainers()) { if (linkedContainer === rv.container || rv.container.isChildOf(linkedContainer)) { rv.allowDrop = false; break; } } } } else if (draggedObj instanceof Parameter) { if (this.data instanceof Parameter) { let parent = this.data.getParent(); if (parent !== null) { if (parent.getValue('type') === Parameter.type.array) { if (draggedObj.getValue('type') !== Parameter.type.array && draggedObj.getValue('type') !== Parameter.type.map && draggedObj.getValue('type') !== Parameter.type.sum && draggedObj.getValue('type') !== Parameter.type.average) { rv.allowDrop = true; dropIntoParent = true; } } else if (parent.getValue('type') === Parameter.type.map) { if (draggedObj.getValue('type') !== Parameter.type.array && draggedObj.getValue('type') !== Parameter.type.map) { rv.allowDrop = true; dropIntoParent = true; } } } else { rv.allowDrop = true; dropIntoParent = true; } } else if (this.panelName === 'parameter') { rv.allowDrop = true; } } else if (draggedObj instanceof Style) { if (this.data instanceof Style) { rv.allowDrop = true; dropIntoParent = true; } else if (this.panelName === 'style') { rv.allowDrop = true; } } if (rv.allowDrop) { if (dropIntoParent) { rv.panel = this.getParent(); rv.position = this.getSiblingPosition() + 1; } else { rv.panel = this; rv.position = 0; } if (rv.panel === null || (rv.panel === draggedObj.getPanelItem().getParent() && rv.position === draggedObj.getPanelItem().getSiblingPosition())) { // do not allow drop if object is not moved (same parent and position) rv.allowDrop = false; } } } return rv; } isDocElementPanel() { return this.panelName === DocElement.type.text || this.panelName === DocElement.type.image || this.panelName === DocElement.type.line || this.panelName === DocElement.type.table || this.panelName === DocElement.type.pageBreak || this.panelName === DocElement.type.barCode || this.panelName === DocElement.type.frame || this.panelName === DocElement.type.section || this.panelName === DocElement.type.watermarkText || this.panelName === DocElement.type.watermarkImage; } }