UNPKG

@pdfme/schemas

Version:

TypeScript base PDF generator and React base UI. Open source, developed by the community, and completely free to use under the MIT license!

368 lines 16.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.uiRender = void 0; const common_1 = require("@pdfme/common"); const tableHelper_js_1 = require("./tableHelper.js"); const helper_js_1 = require("./helper.js"); const cell_js_1 = __importDefault(require("./cell.js")); const buttonSize = 30; function createButton(options) { const button = document.createElement('button'); button.style.width = `${options.width}px`; button.style.height = `${options.height}px`; button.style.position = 'absolute'; button.style.top = options.top; if (options.left !== undefined) { button.style.left = options.left; } if (options.right !== undefined) { button.style.right = options.right; } button.innerText = options.text; button.onclick = options.onClick; return button; } const cellUiRender = cell_js_1.default.ui; const convertToCellStyle = (styles) => ({ fontName: styles.fontName, alignment: styles.alignment, verticalAlignment: styles.verticalAlignment, fontSize: styles.fontSize, lineHeight: styles.lineHeight, characterSpacing: styles.characterSpacing, backgroundColor: styles.backgroundColor, // --- fontColor: styles.textColor, borderColor: styles.lineColor, borderWidth: styles.lineWidth, padding: styles.cellPadding, }); const calcResizedHeadWidthPercentages = (arg) => { const { currentHeadWidthPercentages, currentHeadWidths, changedHeadWidth, changedHeadIndex } = arg; const headWidthPercentages = [...currentHeadWidthPercentages]; const totalWidth = currentHeadWidths.reduce((a, b) => a + b, 0); const changedWidthPercentage = (changedHeadWidth / totalWidth) * 100; const originalNextWidthPercentage = headWidthPercentages[changedHeadIndex + 1] ?? 0; const adjustment = headWidthPercentages[changedHeadIndex] - changedWidthPercentage; headWidthPercentages[changedHeadIndex] = changedWidthPercentage; if (changedHeadIndex + 1 < headWidthPercentages.length) { headWidthPercentages[changedHeadIndex + 1] = originalNextWidthPercentage + adjustment; } return headWidthPercentages; }; const setBorder = (div, borderPosition, arg) => { div.style[`border${borderPosition}`] = `${String(arg.schema.tableStyles.borderWidth)}mm solid ${arg.schema.tableStyles.borderColor}`; }; const drawBorder = (div, row, colIndex, rowIndex, rowsLength, arg) => { const isFirstColumn = colIndex === 0; const isLastColumn = colIndex === Object.values(row.cells).length - 1; const isLastRow = rowIndex === rowsLength - 1; if (row.section === 'head') { setBorder(div, 'Top', arg); if (isFirstColumn) setBorder(div, 'Left', arg); if (isLastColumn) setBorder(div, 'Right', arg); if (JSON.parse(arg.value || '[]').length === 0) { setBorder(div, 'Bottom', arg); } } else if (row.section === 'body') { if (!arg.schema.showHead && rowIndex === 0) { setBorder(div, 'Top', arg); } if (isFirstColumn) setBorder(div, 'Left', arg); if (isLastColumn) setBorder(div, 'Right', arg); if (isLastRow) setBorder(div, 'Bottom', arg); } }; const renderRowUi = (args) => { const { rows, arg, onChangeEditingPosition, offsetY = 0, editingPosition } = args; const value = JSON.parse(arg.value || '[]'); let rowOffsetY = offsetY; rows.forEach((row, rowIndex) => { const { cells, height, section } = row; let colOffsetX = 0; Object.values(cells).forEach((cell, colIndex) => { const div = document.createElement('div'); div.style.position = 'absolute'; div.style.top = `${rowOffsetY}mm`; div.style.left = `${colOffsetX}mm`; div.style.width = `${cell.width}mm`; div.style.height = `${cell.height}mm`; div.style.boxSizing = 'border-box'; drawBorder(div, row, colIndex, rowIndex, rows.length, arg); div.style.cursor = arg.mode === 'designer' || (arg.mode === 'form' && section === 'body') ? 'text' : 'default'; div.addEventListener('click', () => { if (arg.mode === 'viewer') return; onChangeEditingPosition({ rowIndex, colIndex }); }); arg.rootElement.appendChild(div); const isEditing = editingPosition.rowIndex === rowIndex && editingPosition.colIndex === colIndex; let mode = 'viewer'; if (arg.mode === 'form') { mode = section === 'body' && isEditing && !arg.schema.readOnly ? 'designer' : 'viewer'; } else if (arg.mode === 'designer') { mode = isEditing ? 'designer' : 'form'; } void cellUiRender({ ...arg, stopEditing: () => { if (arg.mode === 'form') { resetEditingPosition(); } }, mode, onChange: (v) => { if (!arg.onChange) return; const newValue = (Array.isArray(v) ? v[0].value : v.value); if (section === 'body') { const startRange = arg.schema.__bodyRange?.start ?? 0; value[rowIndex + startRange][colIndex] = newValue; arg.onChange({ key: 'content', value: JSON.stringify(value) }); } else { const newHead = [...arg.schema.head]; newHead[colIndex] = newValue; arg.onChange({ key: 'head', value: newHead }); } }, value: cell.raw, placeholder: '', rootElement: div, schema: { name: '', type: 'cell', content: cell.raw, position: { x: colOffsetX, y: rowOffsetY }, width: cell.width, height: cell.height, ...convertToCellStyle(cell.styles), }, }); colOffsetX += cell.width; }); rowOffsetY += height; }); }; const headEditingPosition = { rowIndex: -1, colIndex: -1 }; const bodyEditingPosition = { rowIndex: -1, colIndex: -1 }; const resetEditingPosition = () => { headEditingPosition.rowIndex = -1; headEditingPosition.colIndex = -1; bodyEditingPosition.rowIndex = -1; bodyEditingPosition.colIndex = -1; }; const uiRender = async (arg) => { const { rootElement, onChange, schema, value, mode, scale } = arg; const body = (0, helper_js_1.getBody)(value); const bodyWidthRange = (0, helper_js_1.getBodyWithRange)(value, schema.__bodyRange); const table = await (0, tableHelper_js_1.createSingleTable)(bodyWidthRange, arg); const showHead = table.settings.showHead; rootElement.innerHTML = ''; const handleChangeEditingPosition = (newPosition, editingPosition) => { resetEditingPosition(); editingPosition.rowIndex = newPosition.rowIndex; editingPosition.colIndex = newPosition.colIndex; void (0, exports.uiRender)(arg); }; if (showHead) { renderRowUi({ rows: table.head, arg, editingPosition: headEditingPosition, onChangeEditingPosition: (p) => handleChangeEditingPosition(p, headEditingPosition), }); } const offsetY = showHead ? table.getHeadHeight() : 0; renderRowUi({ rows: table.body, arg, editingPosition: bodyEditingPosition, onChangeEditingPosition: (p) => { handleChangeEditingPosition(p, bodyEditingPosition); }, offsetY, }); const createAddRowButton = () => createButton({ width: buttonSize, height: buttonSize, top: `${table.getHeight()}mm`, left: `calc(50% - ${buttonSize / 2}px)`, text: '+', onClick: () => { const newRow = Array(schema.head.length).fill(''); if (onChange) onChange({ key: 'content', value: JSON.stringify(body.concat([newRow])) }); }, }); const createRemoveRowButtons = () => { let offsetY = showHead ? table.getHeadHeight() : 0; return table.body.map((row, i) => { offsetY = offsetY + row.height; const removeRowButton = createButton({ width: buttonSize, height: buttonSize, top: `${offsetY - (0, common_1.px2mm)(buttonSize)}mm`, right: `-${buttonSize}px`, text: '-', onClick: () => { const newTableBody = body.filter((_, j) => j !== i + (schema.__bodyRange?.start ?? 0)); if (onChange) onChange({ key: 'content', value: JSON.stringify(newTableBody) }); }, }); return removeRowButton; }); }; if (mode === 'form' && onChange && !schema.readOnly) { if (schema.__bodyRange?.end === undefined || schema.__bodyRange.end >= JSON.parse(value || '[]').length) { rootElement.appendChild(createAddRowButton()); } createRemoveRowButtons().forEach((button) => rootElement.appendChild(button)); } if (mode === 'designer' && onChange) { const addColumnButton = createButton({ width: buttonSize, height: buttonSize, top: `${(showHead ? table.getHeadHeight() : 0) - (0, common_1.px2mm)(buttonSize)}mm`, right: `-${buttonSize}px`, text: '+', onClick: (e) => { e.preventDefault(); const newColumnWidthPercentage = 25; const totalCurrentWidth = schema.headWidthPercentages.reduce((acc, width) => acc + width, 0); const scalingRatio = (100 - newColumnWidthPercentage) / totalCurrentWidth; const scaledWidths = schema.headWidthPercentages.map((width) => width * scalingRatio); onChange([ { key: 'head', value: schema.head.concat(`Head ${schema.head.length + 1}`) }, { key: 'headWidthPercentages', value: scaledWidths.concat(newColumnWidthPercentage) }, { key: 'content', value: JSON.stringify(bodyWidthRange.map((row, i) => row.concat(`Row ${i + 1}`))), }, ]); }, }); rootElement.appendChild(addColumnButton); rootElement.appendChild(createAddRowButton()); createRemoveRowButtons().forEach((button) => rootElement.appendChild(button)); let offsetX = 0; table.columns.forEach((column, i, columns) => { if (columns.length === 1) return; offsetX = offsetX + column.width; const removeColumnButton = createButton({ width: buttonSize, height: buttonSize, top: `${-buttonSize}px`, left: `${offsetX - (0, common_1.px2mm)(buttonSize)}mm`, text: '-', onClick: () => { const totalWidthMinusRemoved = schema.headWidthPercentages.reduce((sum, width, j) => (j !== i ? sum + width : sum), 0); // TODO Should also remove the deleted columnStyles when deleting onChange([ { key: 'head', value: schema.head.filter((_, j) => j !== i) }, { key: 'headWidthPercentages', value: schema.headWidthPercentages .filter((_, j) => j !== i) .map((width) => (width / totalWidthMinusRemoved) * 100), }, { key: 'content', value: JSON.stringify(bodyWidthRange.map((row) => row.filter((_, j) => j !== i))), }, ]); }, }); rootElement.appendChild(removeColumnButton); if (i === table.columns.length - 1) return; const dragHandle = document.createElement('div'); const lineWidth = 5; dragHandle.style.width = `${lineWidth}px`; dragHandle.style.height = '100%'; dragHandle.style.backgroundColor = '#eee'; dragHandle.style.opacity = '0.5'; dragHandle.style.cursor = 'col-resize'; dragHandle.style.position = 'absolute'; dragHandle.style.zIndex = '10'; dragHandle.style.left = `${offsetX - (0, common_1.px2mm)(lineWidth) / 2}mm`; dragHandle.style.top = '0'; const setColor = (e) => { const handle = e.target; handle.style.backgroundColor = '#2196f3'; }; const resetColor = (e) => { const handle = e.target; handle.style.backgroundColor = '#eee'; }; dragHandle.addEventListener('mouseover', setColor); dragHandle.addEventListener('mouseout', resetColor); const prevColumnLeft = offsetX - column.width; const nextColumnRight = offsetX - (0, common_1.px2mm)(lineWidth) + table.columns[i + 1].width; dragHandle.addEventListener('mousedown', (e) => { resetEditingPosition(); const handle = e.target; dragHandle.removeEventListener('mouseover', setColor); dragHandle.removeEventListener('mouseout', resetColor); const startClientX = e.clientX; const startLeft = Number(handle.style.left.replace('mm', '')); let move = 0; const mouseMove = (e) => { const deltaX = e.clientX - startClientX; const moveX = deltaX / common_1.ZOOM / scale; let newLeft = startLeft + moveX; if (newLeft < prevColumnLeft) { newLeft = prevColumnLeft; } if (newLeft >= nextColumnRight) { newLeft = nextColumnRight; } handle.style.left = `${newLeft}mm`; move = newLeft - startLeft; }; rootElement.addEventListener('mousemove', mouseMove); const commitResize = () => { if (move !== 0) { const newHeadWidthPercentages = calcResizedHeadWidthPercentages({ currentHeadWidthPercentages: schema.headWidthPercentages, currentHeadWidths: table.columns.map((column) => column.width), changedHeadWidth: table.columns[i].width + move, changedHeadIndex: i, }); onChange({ key: 'headWidthPercentages', value: newHeadWidthPercentages }); } move = 0; dragHandle.addEventListener('mouseover', setColor); dragHandle.addEventListener('mouseout', resetColor); rootElement.removeEventListener('mousemove', mouseMove); rootElement.removeEventListener('mouseup', commitResize); }; rootElement.addEventListener('mouseup', commitResize); }); rootElement.appendChild(dragHandle); }); } if (mode === 'viewer') { resetEditingPosition(); } const tableHeight = showHead ? table.getHeight() : table.getBodyHeight(); if (schema.height !== tableHeight && onChange) { onChange({ key: 'height', value: tableHeight }); } }; exports.uiRender = uiRender; //# sourceMappingURL=uiRender.js.map