mirror of
https://github.com/documenso/documenso.git
synced 2025-11-12 15:53:02 +10:00
This PR is handles the changes required to support envelopes. The new envelope editor/signing page will be hidden during release. The core changes here is to migrate the documents and templates model to a centralized envelopes model. Even though Documents and Templates are removed, from the user perspective they will still exist as we remap envelopes to documents and templates.
159 lines
3.8 KiB
TypeScript
159 lines
3.8 KiB
TypeScript
import type { Signature } from '@prisma/client';
|
|
import { type Field } from '@prisma/client';
|
|
import type Konva from 'konva';
|
|
|
|
import type { TRecipientColor } from '@documenso/ui/lib/recipient-colors';
|
|
|
|
import type { TFieldMetaSchema } from '../../types/field-meta';
|
|
|
|
export const MIN_FIELD_HEIGHT_PX = 12;
|
|
export const MIN_FIELD_WIDTH_PX = 36;
|
|
|
|
export type FieldToRender = Pick<
|
|
Field,
|
|
'envelopeItemId' | 'recipientId' | 'type' | 'page' | 'customText' | 'inserted' | 'recipientId'
|
|
> & {
|
|
renderId: string; // A unique ID for the field in the render.
|
|
width: number;
|
|
height: number;
|
|
positionX: number;
|
|
positionY: number;
|
|
fieldMeta?: TFieldMetaSchema | null;
|
|
signature?: Signature | null;
|
|
};
|
|
|
|
export type RenderFieldElementOptions = {
|
|
pageLayer: Konva.Layer;
|
|
pageWidth: number;
|
|
pageHeight: number;
|
|
mode?: 'edit' | 'sign' | 'export';
|
|
editable?: boolean;
|
|
color?: TRecipientColor;
|
|
};
|
|
|
|
/**
|
|
* Converts a fields percentage based values to pixel based values.
|
|
*/
|
|
export const calculateFieldPosition = (
|
|
field: Pick<FieldToRender, 'width' | 'height' | 'positionX' | 'positionY'>,
|
|
pageWidth: number,
|
|
pageHeight: number,
|
|
) => {
|
|
const fieldWidth = pageWidth * (Number(field.width) / 100);
|
|
const fieldHeight = pageHeight * (Number(field.height) / 100);
|
|
|
|
const fieldX = pageWidth * (Number(field.positionX) / 100);
|
|
const fieldY = pageHeight * (Number(field.positionY) / 100);
|
|
|
|
return { fieldX, fieldY, fieldWidth, fieldHeight };
|
|
};
|
|
|
|
type ConvertPixelToPercentageOptions = {
|
|
positionX: number;
|
|
positionY: number;
|
|
width: number;
|
|
height: number;
|
|
pageWidth: number;
|
|
pageHeight: number;
|
|
};
|
|
|
|
export const convertPixelToPercentage = (options: ConvertPixelToPercentageOptions) => {
|
|
const { positionX, positionY, width, height, pageWidth, pageHeight } = options;
|
|
|
|
const fieldX = (positionX / pageWidth) * 100;
|
|
const fieldY = (positionY / pageHeight) * 100;
|
|
|
|
const fieldWidth = (width / pageWidth) * 100;
|
|
const fieldHeight = (height / pageHeight) * 100;
|
|
|
|
return { fieldX, fieldY, fieldWidth, fieldHeight };
|
|
};
|
|
|
|
type CalculateMultiItemPositionOptions = {
|
|
/**
|
|
* The field width in pixels.
|
|
*/
|
|
fieldWidth: number;
|
|
|
|
/**
|
|
* The field height in pixels.
|
|
*/
|
|
fieldHeight: number;
|
|
|
|
/**
|
|
* Total amount of items that will be rendered.
|
|
*/
|
|
itemCount: number;
|
|
|
|
/**
|
|
* The position of the item in the list.
|
|
*
|
|
* Starts from 0
|
|
*/
|
|
itemIndex: number;
|
|
|
|
/**
|
|
* The size of the item input, example checkbox box, radio button, etc.
|
|
*/
|
|
itemSize: number;
|
|
|
|
/**
|
|
* The spacing between the item and text.
|
|
*/
|
|
spacingBetweenItemAndText: number;
|
|
|
|
/**
|
|
* The inner padding of the field.
|
|
*/
|
|
fieldPadding: number;
|
|
|
|
type: 'checkbox' | 'radio';
|
|
};
|
|
|
|
/**
|
|
* Calculate the position of a field item such as Checkbox, Radio.
|
|
*/
|
|
export const calculateMultiItemPosition = (options: CalculateMultiItemPositionOptions) => {
|
|
const {
|
|
fieldWidth,
|
|
fieldHeight,
|
|
itemCount,
|
|
itemIndex,
|
|
itemSize,
|
|
spacingBetweenItemAndText,
|
|
fieldPadding,
|
|
type,
|
|
} = options;
|
|
|
|
const innerFieldHeight = fieldHeight - fieldPadding * 2;
|
|
const innerFieldWidth = fieldWidth - fieldPadding; // This is purposefully not using fullPadding to allow flush text.
|
|
const innerFieldX = fieldPadding;
|
|
const innerFieldY = fieldPadding;
|
|
|
|
const itemHeight = innerFieldHeight / itemCount;
|
|
|
|
const y = itemIndex * itemHeight + innerFieldY;
|
|
|
|
let itemInputY = y + itemHeight / 2 - itemSize / 2;
|
|
let itemInputX = innerFieldX;
|
|
|
|
if (type === 'radio') {
|
|
itemInputX = innerFieldX + itemSize / 2;
|
|
itemInputY = y + itemHeight / 2;
|
|
}
|
|
|
|
const textX = innerFieldX + itemSize + spacingBetweenItemAndText;
|
|
const textY = y;
|
|
const textWidth = innerFieldWidth - itemSize - spacingBetweenItemAndText;
|
|
const textHeight = itemHeight;
|
|
|
|
return {
|
|
itemInputX,
|
|
itemInputY,
|
|
textX,
|
|
textY,
|
|
textWidth,
|
|
textHeight,
|
|
};
|
|
};
|