mirror of
https://github.com/documenso/documenso.git
synced 2025-11-09 20:12:31 +10:00
187 lines
4.3 KiB
TypeScript
187 lines
4.3 KiB
TypeScript
import Konva from 'konva';
|
|
|
|
import {
|
|
DEFAULT_RECT_BACKGROUND,
|
|
RECIPIENT_COLOR_STYLES,
|
|
} from '@documenso/ui/lib/recipient-colors';
|
|
|
|
import type { FieldToRender, RenderFieldElementOptions } from './field-renderer';
|
|
import { calculateFieldPosition } from './field-renderer';
|
|
|
|
export const konvaTextFontFamily =
|
|
'Noto Sans, Noto Sans Japanese, Noto Sans Chinese, Noto Sans Korean, sans-serif';
|
|
export const konvaTextFill = 'black';
|
|
|
|
export const upsertFieldGroup = (
|
|
field: FieldToRender,
|
|
options: RenderFieldElementOptions,
|
|
): Konva.Group => {
|
|
const { pageWidth, pageHeight, pageLayer, editable, scale } = options;
|
|
|
|
const { fieldX, fieldY, fieldWidth, fieldHeight } = calculateFieldPosition(
|
|
field,
|
|
pageWidth,
|
|
pageHeight,
|
|
);
|
|
|
|
const fieldGroup: Konva.Group =
|
|
pageLayer.findOne(`#${field.renderId}`) ||
|
|
new Konva.Group({
|
|
id: field.renderId,
|
|
name: 'field-group',
|
|
});
|
|
|
|
const maxXPosition = (pageWidth - fieldWidth) * scale;
|
|
const maxYPosition = (pageHeight - fieldHeight) * scale;
|
|
|
|
fieldGroup.setAttrs({
|
|
scaleX: 1,
|
|
scaleY: 1,
|
|
x: fieldX,
|
|
y: fieldY,
|
|
draggable: editable,
|
|
dragBoundFunc: (pos) => {
|
|
const newX = Math.max(0, Math.min(maxXPosition, pos.x));
|
|
const newY = Math.max(0, Math.min(maxYPosition, pos.y));
|
|
|
|
return { x: newX, y: newY };
|
|
},
|
|
} satisfies Partial<Konva.GroupConfig>);
|
|
|
|
return fieldGroup;
|
|
};
|
|
|
|
export const upsertFieldRect = (
|
|
field: FieldToRender,
|
|
options: RenderFieldElementOptions,
|
|
): Konva.Rect => {
|
|
const { pageWidth, pageHeight, mode, pageLayer, color } = options;
|
|
|
|
const { fieldWidth, fieldHeight } = calculateFieldPosition(field, pageWidth, pageHeight);
|
|
|
|
const fieldRect: Konva.Rect =
|
|
pageLayer.findOne(`#${field.renderId}-rect`) ||
|
|
new Konva.Rect({
|
|
id: `${field.renderId}-rect`,
|
|
name: 'field-rect',
|
|
});
|
|
|
|
fieldRect.setAttrs({
|
|
width: fieldWidth,
|
|
height: fieldHeight,
|
|
fill: DEFAULT_RECT_BACKGROUND,
|
|
stroke: color ? RECIPIENT_COLOR_STYLES[color].baseRing : '#e5e7eb',
|
|
strokeWidth: 2,
|
|
cornerRadius: 2,
|
|
strokeScaleEnabled: false,
|
|
visible: mode !== 'export',
|
|
} satisfies Partial<Konva.RectConfig>);
|
|
|
|
return fieldRect;
|
|
};
|
|
|
|
export const createSpinner = ({
|
|
fieldWidth,
|
|
fieldHeight,
|
|
}: {
|
|
fieldWidth: number;
|
|
fieldHeight: number;
|
|
}) => {
|
|
const loadingGroup = new Konva.Group({
|
|
name: 'loading-spinner-group',
|
|
});
|
|
|
|
const rect = new Konva.Rect({
|
|
x: 4,
|
|
y: 4,
|
|
width: fieldWidth - 8,
|
|
height: fieldHeight - 8,
|
|
fill: 'white',
|
|
opacity: 0.8,
|
|
});
|
|
|
|
const maxSpinnerSize = 10;
|
|
const smallerDimension = Math.min(fieldWidth, fieldHeight);
|
|
const spinnerSize = Math.min(smallerDimension, maxSpinnerSize);
|
|
|
|
const spinner = new Konva.Arc({
|
|
x: fieldWidth / 2,
|
|
y: fieldHeight / 2,
|
|
innerRadius: spinnerSize,
|
|
outerRadius: spinnerSize / 2,
|
|
angle: 270,
|
|
rotation: 0,
|
|
fill: 'rgba(122, 195, 85, 1)',
|
|
lineCap: 'round',
|
|
});
|
|
|
|
rect.moveToTop();
|
|
spinner.moveToTop();
|
|
|
|
loadingGroup.add(rect);
|
|
loadingGroup.add(spinner);
|
|
|
|
const anim = new Konva.Animation((frame) => {
|
|
spinner.rotate(180 * (frame.timeDiff / 500));
|
|
});
|
|
|
|
anim.start();
|
|
|
|
return loadingGroup;
|
|
};
|
|
|
|
type CreateFieldHoverInteractionOptions = {
|
|
options: RenderFieldElementOptions;
|
|
fieldGroup: Konva.Group;
|
|
fieldRect: Konva.Rect;
|
|
};
|
|
|
|
/**
|
|
* Adds smooth transition-like behavior for hover effects to the field group and rectangle.
|
|
*/
|
|
export const createFieldHoverInteraction = ({
|
|
options,
|
|
fieldGroup,
|
|
fieldRect,
|
|
}: CreateFieldHoverInteractionOptions) => {
|
|
const { mode } = options;
|
|
|
|
if (mode === 'export' || !options.color) {
|
|
return;
|
|
}
|
|
|
|
const hoverColor = RECIPIENT_COLOR_STYLES[options.color].baseRingHover;
|
|
|
|
fieldGroup.on('mouseover', () => {
|
|
new Konva.Tween({
|
|
node: fieldRect,
|
|
duration: 0.3,
|
|
fill: hoverColor,
|
|
}).play();
|
|
});
|
|
|
|
fieldGroup.on('mouseout', () => {
|
|
new Konva.Tween({
|
|
node: fieldRect,
|
|
duration: 0.3,
|
|
fill: DEFAULT_RECT_BACKGROUND,
|
|
}).play();
|
|
});
|
|
|
|
fieldGroup.on('transformstart', () => {
|
|
new Konva.Tween({
|
|
node: fieldRect,
|
|
duration: 0.3,
|
|
fill: hoverColor,
|
|
}).play();
|
|
});
|
|
|
|
fieldGroup.on('transformend', () => {
|
|
new Konva.Tween({
|
|
node: fieldRect,
|
|
duration: 0.3,
|
|
fill: DEFAULT_RECT_BACKGROUND,
|
|
}).play();
|
|
});
|
|
};
|