vue-data-ui
Version:
A user-empowering data visualization Vue 3 components library for eloquent data storytelling
1,162 lines (1,159 loc) • 106 kB
JavaScript
import k from "./html2canvas.esm-Dr4iCOK6.js";
import { E as p } from "./jspdf.es.min-eTA26cmm.js";
import { z as v, A, u as w, B as M } from "./index-CHWA6Lnw.js";
import { createElementBlock as d, openBlock as n, createElementVNode as r, normalizeStyle as c, toDisplayString as u, createCommentVNode as a, normalizeClass as y, createStaticVNode as F, withDirectives as N, vModelCheckbox as C, vModelText as x, renderSlot as O, Fragment as L, renderList as G } from "vue";
import { _ } from "./_plugin-vue_export-helper-CHgC5LLL.js";
const m = {
props: {
config: {
type: Object,
default() {
return {};
}
},
dataset: {
type: Object,
default() {
return {
shapes: [],
lastSelectedShape: void 0
};
}
}
},
data() {
return {
activeShape: void 0,
strokeSize: 1,
currentPointer: {
start: {
x: 0,
y: 0
},
end: {
x: 0,
end: 0
}
},
currentTarget: void 0,
hoveredShapeId: void 0,
isBold: !1,
isBulletTextMode: !1,
isDash: !1,
isDeleteMode: !1,
isDrawing: !1,
isDrawingNewShape: !0,
isDrawMode: !1,
isItalic: !1,
isMouseDown: !1,
isMoveMode: !1,
isPrinting: !1,
isResizeMode: !1,
isSelectMode: !1,
isSummaryOpen: !1,
isTextMode: !1,
isUnderline: !1,
isWriting: !1,
lastSelectedShape: this.dataset.lastSelectedShape,
pointerDownId: -1,
pointerPosition: {
x: 0,
y: 0
},
preventEdit: !0,
selectedGroup: [],
shapes: this.dataset.shapes ? this.dataset.shapes : [],
shapesOrder: [],
step: Math.round(Math.random()) * 1e5,
svgHeight: 1e3,
svgWidth: 1e3,
options: {
arrow: {
color: "grey",
filled: !0
},
circle: {
color: "grey",
filled: !1,
radius: 3,
strokeWidth: 2
},
rect: {
color: "grey",
filled: !1,
strokeWidth: 2,
height: 12,
width: 12
}
},
selectedColor: "#000000",
showCaret: !1,
sizeRatio: 1,
slottedSvg: void 0,
sourceWidth: 1,
sourceHeight: 1,
textAlign: "start",
textFont: 20,
transparency: 100,
transparencyCodes: M
};
},
watch: {
shapes: {
handler(t) {
t.length === 0 && (this.lastSelectedShape = void 0);
}
}
},
computed: {
FINAL_CONFIG() {
const t = w().vue_ui_annotator;
if (!Object.keys(this.config || {}).length)
return t;
const i = this.treeShake({
defaultConfig: t,
userConfig: this.config
});
return this.convertConfigColors(i);
},
canSelect() {
return this.shapes.filter((t) => !["line", "group"].includes(t.type)).length > 1;
},
colorTransparency() {
return this.transparencyCodes[this.transparency > 98 ? 98 : this.transparency];
},
cursorClass() {
switch (!0) {
case this.isDeleteMode:
return "default";
case this.isMoveMode:
return "move";
case this.isTextMode:
return "text";
case this.isResizeMode:
return "se-resize";
default:
return "";
}
},
records() {
return this.shapes;
},
userShapes() {
return this.records.map((t) => {
switch (!0) {
case (t && t.type === "arrow"):
const i = t.strokeWidth > 3 ? 5 : 10, o = t.strokeWidth > 3 ? 2.5 : 5;
return `
<defs>
<marker
id="${t.id}"
markerWidth="${i}"
markerHeight="${i}"
refX="0"
refY="${o}"
orient="auto"
>
<polygon
points="0 0,${i} ${o}, 0 ${i}"
fill="${t.color}"
/>
</marker>
</defs>
${this.includeSelectionIndicator(t)}
<g id="${t.id}">
<path
style="stroke-linecap: round !important; ${t.isDash ? `stroke-dasharray: ${t.strokeWidth * 3}` : ""}"
stroke="${t.color}"
id="${t.id}"
d="M${t.x},${t.y} ${t.endX},${t.endY}"
stroke-width="${t.strokeWidth}"
marker-end="url(#${t.id})"
/>
</g>
<g id="${t.id}">
<rect
id="${t.id}"
x="${t.x - 10}"
y="${t.y - 10}"
height="20"
width="20"
fill="rgba(0,0,0,0.3)"
style="display:${this.isResizeMode || this.isMoveMode ? "initial" : "none"}; rx:1 !important; ry:1 !important;"
/>
</g>
${this.includeDeleteButton(t)}
</g>
`;
case (t && t.type === "circle"):
return `
<g id="${t.id}">
${this.includeSelectionIndicator(t)}
<circle
id="${t.id}"
cx="${t.x}"
cy="${t.y}"
r="${t.circleRadius ? t.circleRadius : Number.MIN_VALUE}"
fill="${t.isFilled ? t.color + t.alpha : "rgba(255,255,255,0.001)"}"
stroke="${t.color + t.alpha}"
stroke-width="${t.strokeWidth}"
style="${t.isDash ? `stroke-dasharray: ${t.strokeWidth * 3}` : ""}"
>
</circle>
</g>
${this.includeDeleteButton(t)}`;
case (t && t.type === "group"):
return `<g id="${t.id}">
<rect
id="${this.isResizeMode ? "" : t.id}"
x="${t.x}"
y="${t.y}"
fill="transparent"
height="${t.rectHeight}"
width="${t.rectWidth}"
stroke="grey"
stroke-width="1"
style="rx:1 !important; ry:1 !important; ${t.isDash ? `stroke-dasharray: ${t.strokeWidth * 3}` : ""}; display:${this.isSelectMode || this.isDeleteMode || this.hoveredShapeId && this.hoveredShapeId === t.id ? "initial" : "none"};"
/>
<g id="${t.id}">
${t.content ? t.content : ""}
</g>
${this.includeDeleteButton(t)}
</g> `;
case (t && t.type === "rect"):
return `<g id="${t.id}">
${this.includeSelectionIndicator(t)}
<rect
id="${this.isResizeMode ? "" : t.id}"
x="${t.x}"
y="${t.y}"
fill="${t.isFilled ? t.color + t.alpha : "rgba(255,255,255,0.001)"}"
height="${t.rectHeight}"
width="${t.rectWidth}"
stroke="${t.color + t.alpha}"
stroke-width="${t.strokeWidth}"
style="rx:1 !important; ry:1 !important; ${t.isDash ? `stroke-dasharray: ${t.strokeWidth * 3}` : ""}"
/>
<rect id="${t.id}"
x="${t.x + t.rectWidth}"
y="${t.y + t.rectHeight}"
height="20"
width="20"
fill="rgba(0,0,0,0.3)"
style="display:${this.isResizeMode ? "initial" : "none"}; rx:1 !important; ry:1 !important;"
/>
${this.includeDeleteButton(t)}
</g> `;
case (t && t.type === "line"):
return `
<g id="${t.id}">
<path
id="${t.id}"
d="M${t.path ? t.path : ""}"
style="stroke:${t.color + t.alpha} !important; fill:none; stroke-width:${t.strokeWidth} !important; stroke-linecap: round !important; stroke-linejoin: round !important;"
/>
${this.includeDeleteButton(t)}
</g>
`;
case (t && t.type === "text"):
const h = t.textContent.split(""), s = [];
for (let e = 0; e < h.length; e += 1)
s.push(`
${t.isBulletTextMode ? `<tspan x="${t.x - t.fontSize}" y="${t.y + t.fontSize * e}" id="${t.id}" font-size="${t.fontSize / 2}">⬤</tspan>` : ""}
<tspan id="${t.id}" x="${t.x}" y="${t.y + t.fontSize * e}">
${h[e]}
</tspan>`);
return `
${this.includeSelectionIndicator(t)}
${this.computeTextElement(
t,
s,
t.isBulletTextMode
)}
`;
}
});
}
},
mounted() {
const t = this.$refs.drawSvgContainer;
let i = !1;
this.walkTheDOM(t, (s) => {
if (!i && ["DIV", "svg", "section", "canvas"].includes(s.tagName)) {
this.slottedSvg = s, i = !0;
return;
}
});
const o = this.slottedSvg.getBoundingClientRect();
this.sizeRatio = o.height / o.width, this.svgWidth = 1e3, this.svgHeight = this.sizeRatio * 1e3, this.sourceWidth = o.width, this.sourceHeight = o.height, new ResizeObserver((s) => {
s.forEach((e) => {
this.sourceWidth = e.contentRect.width, this.sourceHeight = e.contentRect.height, this.sizeRatio = e.contentRect.height / e.contentRect.width, this.svgHeight = this.sizeRatio * 1e3;
});
}).observe(this.slottedSvg), window.addEventListener("keydown", (s) => {
this.write(s);
});
},
destroyed() {
window.removeEventListener("keydown", (t) => {
this.write(t);
});
},
methods: {
treeShake: A,
convertConfigColors: v,
bringShapeTo(t) {
const i = this.shapes.find(
(o) => o.id === this.lastSelectedShape.id
);
switch (!0) {
case t === "front":
this.shapes = this.shapes.filter((o) => o.id !== i.id), this.shapes.push(i);
break;
case t === "back":
this.shapes = this.shapes.filter((o) => o.id !== i.id), this.shapes = [i, ...this.shapes];
break;
default:
return;
}
},
clickSvg(t) {
if (this.isDeleteMode)
return;
this.deleteEmptyTextElement(), this.isTextMode ? (this.isWriting = !0, this.showCaret = !0) : (this.isWriting = !1, this.showCaret = !1, this.isTextMode = !1);
let i = `text_${Math.random() * 1e4}_${Math.random() * 99999}`;
if (this.isWriting) {
this.shapes.push({
id: i,
type: "text",
lines: 0,
x: this.pointerPosition.x,
y: this.pointerPosition.y,
textContent: "",
fontSize: this.copy(this.textFont),
textAlign: this.copy(this.textAlign),
isBold: this.copy(this.isBold),
isItalic: this.copy(this.isItalic),
isUnderline: this.copy(this.isUnderline),
color: this.copy(this.selectedColor),
isBulletTextMode: this.copy(this.isBulletTextMode)
}), this.currentTarget = this.shapes.at(-1), this.lastSelectedShape = this.shapes.at(-1);
return;
}
const o = () => {
this.isDash = this.shapes.find((s) => s.id === t.target.id).isDash;
}, h = () => {
this.strokeSize = this.shapes.find(
(s) => s.id === t.target.id
).strokeWidth;
};
if (this.isSelectMode = !1, t.target.id.includes("arrow")) {
this.activeShape = "arrow", o(), h();
return;
}
if (t.target.id.includes("circle")) {
this.activeShape = "circle", this.options.circle.filled = this.shapes.find(
(s) => s.id === t.target.id
).isFilled, o(), h();
return;
}
if (t.target.id.includes("rect")) {
this.activeShape = "rect", this.options.rect.filled = this.shapes.find(
(s) => s.id === t.target.id
).isFilled, o(), h();
return;
}
if (t.target.id.includes("line")) {
this.activeShape = "line", h();
return;
}
if (t.target.id.includes("text")) {
this.isTextMode = !0, this.isWriting = !0, this.showCaret = !0;
const s = this.shapes.find((e) => e.id === t.target.id);
s && s.textAlign && (this.textAlign = this.shapes.find(
(e) => e.id === t.target.id
).textAlign), s && (this.isBulletTextMode = this.shapes.find(
(e) => e.id === t.target.id
).isBulletTextMode);
return;
}
},
copyPaste() {
const t = {
...this.lastSelectedShape,
id: `${this.lastSelectedShape.id}_copy`,
x: this.lastSelectedShape.x - 100 < 0 ? 1 : this.lastSelectedShape.x - 100,
y: this.lastSelectedShape.y - 100 < 0 ? 1 : this.lastSelectedShape.y - 100
};
this.shapes.push(t);
},
includeDeleteButton(t, i = !1) {
switch (!0) {
case t.type === "circle":
return `
<g id="${t.id}" style="display:${this.isDeleteMode ? "initial" : "none"};">
<circle id="${t.id}" cx="${t.x}" cy="${t.y}" r="12" fill="red"/>
<line stroke="white" stroke-width="2" id="${t.id}" x1="${t.x - 4}" y1="${t.y - 4}" x2="${t.x + 4}" y2="${t.y + 4}"/>
<line stroke="white" stroke-width="2" id="${t.id}" x1="${t.x + 4}" y1="${t.y - 4}" x2="${t.x - 4}" y2="${t.y + 4}"/>
</g>
`;
case t.type === "text":
let o, h = [-8, -12, -4, -12, -4];
switch (!0) {
case t.textAlign === "start":
i ? o = [-20, -24, -16, -16, -24] : o = [-16, -20, -12, -12, -20];
break;
case t.textAlign === "middle":
o = [0, -4, 4, 4, -4], h = [-32, -36, -28, -36, -28];
break;
case t.textAlign === "end":
o = [16, 20, 12, 12, 20];
break;
default:
o = [0, 0, 0];
break;
}
return `
<g id="${t.id}" style="display:${this.isDeleteMode ? "initial" : "none"};">
<circle id="${t.id}" cx="${t.x + o[0]}" cy="${t.y + h[0]}" r="12" fill="red"/>
<line stroke="white" stroke-width="2" id="${t.id}" x1="${t.x + o[1]}" y1="${t.y + h[1]}" x2="${t.x + o[2]}" y2="${t.y + h[2]}"/>
<line stroke="white" stroke-width="2" id="${t.id}" x1="${t.x + o[3]}" y1="${t.y + h[3]}" x2="${t.x + o[4]}" y2="${t.y + h[4]}"/>
</g>
`;
default:
return `
<g id="${t.id}" style="display:${this.isDeleteMode ? "initial" : "none"};">
<circle id="${t.id}" cx="${t.x - 4}" cy="${t.y - 4}" r="12" fill="red"/>
<line stroke="white" stroke-width="2" id="${t.id}" x1="${t.x - 8}" y1="${t.y - 8}" x2="${t.x}" y2="${t.y}"/>
<line stroke="white" stroke-width="2" id="${t.id}" x1="${t.x}" y1="${t.y - 8}" x2="${t.x - 8}" y2="${t.y}"/>
</g>
`;
}
},
includeSelectionIndicator(t) {
if (t)
switch (!0) {
case t.type === "rect":
return `
<rect
id="${t.id}"
style="stroke-dasharray: 10; display:${this.hoveredShapeId && this.hoveredShapeId === t.id ? "initial" : "none"}"
x="${t.x - 20}"
y="${t.y - 20}"
height="${t.rectHeight + 40}"
width="${t.rectWidth + 40}"
fill="transparent"
stroke="grey"
/>
`;
case t.type === "circle":
return `
<rect
id="${t.id}"
style="stroke-dasharray: 10; display:${this.hoveredShapeId && this.hoveredShapeId === t.id ? "initial" : "none"}"
x="${t.x - t.circleRadius - 20}"
y="${t.y - t.circleRadius - 20}"
height="${t.circleRadius * 2 + 40}"
width="${t.circleRadius * 2 + 40}"
fill="transparent"
stroke="grey"
/>
`;
case t.type === "arrow":
const i = t.endX - t.x > 0, o = t.endY - t.y > 0;
return `
<rect
id="${t.id}"
style="stroke-dasharray: 10; display:${this.hoveredShapeId && this.hoveredShapeId === t.id ? "initial" : "none"}"
x="${i ? t.x - 20 : t.endX - 20}"
y="${o ? t.y - 20 : t.endY - 20}"
height="${o ? t.endY - t.y + 40 : t.y - t.endY + 40}"
width="${i ? t.endX - t.x + 40 : t.x - t.endX + 40}"
fill="transparent"
stroke="grey"
/>
`;
case t.type === "text":
const h = Array.from(document.getElementsByTagName("text")).find(
(I) => I.id === t.id
);
if (!h)
return;
const { x: s, y: e, width: l, height: b } = h.getBBox();
return `
<rect
id="${t.id}"
style="stroke-dasharray: 10; display:${this.hoveredShapeId && this.hoveredShapeId === t.id ? "initial" : "none"}"
x="${s - 20}"
y="${e - 20}"
height="${b + 40}"
width="${l + 40}"
fill="transparent"
stroke="grey"
/>
`;
default:
return "";
}
},
allowEditAndHoverShapes(t) {
t.preventDefault(), this.preventEdit = !1, t.target && t.target.id && (this.hoveredShapeId = t.target.id);
},
setSelectedTextAlignTo(t) {
!this.lastSelectedShape || this.lastSelectedShape.type !== "text" || (this.lastSelectedShape.textAlign = t);
},
undoLastShape() {
this.lastSelectedShape = void 0, this.shapes = this.shapes.slice(0, -1);
},
write(t) {
if (this.preventEdit)
return;
t.preventDefault();
const i = t.keyCode;
if (!this.isWriting)
return;
this.showCaret = !0;
let o;
if (this.lastSelectedShape.type === "text" ? o = this.shapes.find((s) => s.id === this.lastSelectedShape.id) : o = this.shapes.at(-1), this.currentTarget = o, o.type !== "text")
return;
this.currentTarget.isBold = this.copy(this.isBold), this.currentTarget.isItalic = this.copy(this.isItalic), this.currentTarget.isUnderline = this.copy(this.isUnderline);
const h = [
16,
17,
18,
20,
27,
33,
34,
35,
36,
37,
38,
39,
40,
45,
91,
112,
113,
114,
115,
116,
117,
118,
119,
120,
121,
122,
123,
221,
255,
"Unidentified"
];
switch (!0) {
case i === 8:
o.textContent = o.textContent.slice(0, -1);
break;
case i === 9:
o.textContent += " ";
break;
case i === 13:
o.lines += 1, o.textContent += "";
return;
case h.includes(i):
return;
default:
o.textContent += t.key;
}
},
groupShapes() {
if (this.selectedGroup = [], this.activeShape !== "group") {
this.isSelectMode = !1, this.shapes = this.shapes.filter((i) => i.type !== "group");
return;
}
const t = this.shapes.at(-1);
if (this.shapes.forEach((i) => {
if (i.type !== "group")
switch (!0) {
case i.type === "arrow":
const o = i.x <= i.endX && i.y <= i.endY && t.x <= i.x && t.y <= i.y && t.x + t.rectWidth >= i.endX && t.y + t.rectHeight >= i.endY, h = i.endY < i.y && i.x < i.endX && t.x <= i.x && t.y <= i.y && t.x + t.rectWidth >= i.endX && t.y + t.rectHeight >= i.y, s = i.x > i.endX && i.y < i.endY && t.x <= i.endX && t.y <= i.endY && t.x + t.rectWidth >= i.x && t.y + t.rectHeight >= i.endY, e = i.x > i.endX && i.y > i.endY && t.x <= i.endX && t.y <= i.endY && t.x + t.rectWidth >= i.x && t.y + t.rectHeight >= i.y;
(o || h || s || e) && this.selectedGroup.push(i);
break;
case i.type === "circle":
t.x <= i.x + i.circleRadius && t.y <= i.y + i.circleRadius && i.x + i.circleRadius <= t.x + t.rectWidth && i.y + i.circleRadius <= t.y + t.rectHeight && this.selectedGroup.push(i);
break;
case i.type === "rect":
t.x <= i.x && t.y <= i.y && i.x <= t.x + t.rectWidth && i.y <= t.y + t.rectHeight && i.x + i.rectWidth <= t.x + t.rectWidth && i.y + i.rectHeight <= t.y + t.rectHeight && i.rectWidth <= t.rectWidth && i.rectHeight <= t.rectHeight && this.selectedGroup.push(i);
break;
case i.type === "text":
t.x <= i.x && t.y <= i.y && this.selectedGroup.push(i);
break;
}
}), this.selectedGroup = this.selectedGroup.map((i) => ({
...i,
id: t.id,
oldId: i.id,
diffX: i.x - t.x,
diffY: i.y - t.y,
diffEndX: i.endX ? i.endX - t.x : 0,
diffEndY: i.endY ? i.endY - t.y : 0
})), t.source = this.selectedGroup, this.selectedGroup.length > 1) {
const i = this.copy(this.selectedGroup).map((o) => o.oldId);
this.shapes = this.shapes.filter((o) => !i.includes(o.id)), this.selectedGroup.forEach((o) => {
switch (!0) {
case o.type === "circle":
t.content += `
<circle
id="${o.id}"
cx="${o.x}"
cy="${o.y}"
r="${o.circleRadius ? o.circleRadius : Number.MIN_VALUE}"
fill="${o.isFilled ? o.color + o.alpha : "rgba(255,255,255,0.001)"}"
stroke="${o.color + o.alpha}"
stroke-width="${o.strokeWidth}"
style="${o.isDash ? `stroke-dasharray: ${o.strokeWidth * 3}` : ""}"
/>
`;
break;
case o.type === "rect":
t.content += `
<rect
id="${this.isResizeMode ? "" : o.id}"
x="${o.x}"
y="${o.y}"
fill="${o.isFilled ? o.color + o.alpha : "rgba(255,255,255,0.001)"}"
height="${o.rectHeight}"
width="${o.rectWidth}"
stroke="${o.color + o.alpha}"
stroke-width="${o.strokeWidth}"
style="rx:1 !important; ry:1 !important; ${o.isDash ? `stroke-dasharray: ${o.strokeWidth * 3}` : ""}"
/>
`;
break;
case o.type === "arrow":
const h = o.strokeWidth > 3 ? 5 : 10, s = o.strokeWidth > 3 ? 2.5 : 5, e = Date.now();
t.content += `
<g id="${o.id}">
<defs>
<marker
id="${e}"
markerWidth="${h}"
markerHeight="${h}"
refX="0"
refY="${s}"
orient="auto"
>
<polygon
points="0 0,${h} ${s}, 0 ${h}"
fill="${o.color}"
/>
</marker>
</defs>
<path
style="stroke-linecap: round !important; ${o.isDash ? `stroke-dasharray: ${o.strokeWidth * 3}` : ""}"
stroke="${o.color}"
id="${o.id}"
d="M${o.x},${o.y} ${o.endX},${o.endY}"
stroke-width="${o.strokeWidth}"
marker-end="url(#${e})"
/>
</g>
`;
break;
case o.type === "text":
const l = o.textContent.split(""), b = [];
for (let I = 0; I < l.length; I += 1)
b.push(`
${o.isBulletTextMode ? `<tspan x="${o.x - o.fontSize}" y="${o.y + o.fontSize * I}" id="${o.id}" font-size="${o.fontSize / 2}">⬤</tspan>` : ""}
<tspan id="${o.id}" x="${o.x}" y="${o.y + o.fontSize * I}">
${l[I]}
</tspan>`);
t.content += `
${this.computeTextElement(o, b, o.isBulletTextMode)}
`;
break;
}
});
} else
this.shapes = this.shapes.filter((i) => i.id !== t.id);
},
moveGroup(t) {
t.content = "", t.x = this.copy(this.pointerPosition.x) - t.rectWidth / 2, t.y = this.copy(this.pointerPosition.y) - t.rectHeight / 2, t.source.forEach((i) => {
switch (!0) {
case i.type === "circle":
t.content += `
<circle
id="${i.id}"
cx="${this.copy(this.pointerPosition.x) + i.diffX - t.rectWidth / 2}"
cy="${this.copy(this.pointerPosition.y) + i.diffY - t.rectHeight / 2}"
r="${i.circleRadius ? i.circleRadius : Number.MIN_VALUE}"
fill="${i.isFilled ? i.color + i.alpha : "rgba(255,255,255,0.001)"}"
stroke="${i.color + i.alpha}"
stroke-width="${i.strokeWidth}"
style="${i.isDash ? `stroke-dasharray: ${i.strokeWidth * 3}` : ""}"
/>
`;
break;
case i.type === "rect":
t.content += `
<rect
id="${this.isResizeMode ? "" : i.id}"
x="${this.copy(this.pointerPosition.x) + i.diffX - t.rectWidth / 2}"
y="${this.copy(this.pointerPosition.y) + i.diffY - t.rectHeight / 2}"
fill="${i.isFilled ? i.color + i.alpha : "rgba(255,255,255,0.001)"}"
height="${i.rectHeight}"
width="${i.rectWidth}"
stroke="${i.color + i.alpha}"
stroke-width="${i.strokeWidth}"
style="rx:1 !important; ry:1 !important; ${i.isDash ? `stroke-dasharray: ${i.strokeWidth * 3}` : ""}"
/>
`;
break;
case i.type === "arrow":
const o = i.strokeWidth > 3 ? 5 : 10, h = i.strokeWidth > 3 ? 2.5 : 5, s = Date.now();
t.content += `
<g id="${i.id}">
<defs>
<marker
id="${s}"
markerWidth="${o}"
markerHeight="${o}"
refX="0"
refY="${h}"
orient="auto"
>
<polygon
points="0 0,${o} ${h}, 0 ${o}"
fill="${i.color}"
/>
</marker>
</defs>
<path
style="stroke-linecap: round !important; ${i.isDash ? `stroke-dasharray: ${i.strokeWidth * 3}` : ""}"
stroke="${i.color}"
id="${i.id}"
d="M${this.copy(this.pointerPosition.x) + i.diffX - t.rectWidth / 2},${this.copy(this.pointerPosition.y) + i.diffY - t.rectHeight / 2} ${this.copy(this.pointerPosition.x) + i.diffEndX - t.rectWidth / 2},${this.copy(this.pointerPosition.y) + i.diffEndY - t.rectHeight / 2}"
stroke-width="${i.strokeWidth}"
marker-end="url(#${s})"
/>
</g>
`;
break;
case i.type === "text":
const e = i.textContent.split(""), l = [];
for (let b = 0; b < e.length; b += 1)
l.push(`
${i.isBulletTextMode ? `<tspan x="${this.copy(this.pointerPosition.x) + i.diffX - i.fontSize - t.rectWidth / 2}" y="${this.copy(this.pointerPosition.y) + i.diffY + i.fontSize * b - t.rectHeight / 2}" id="${i.id}" font-size="${i.fontSize / 2}">⬤</tspan>` : ""}
<tspan id="${i.id}" x="${this.copy(this.pointerPosition.x) + i.diffX - t.rectWidth / 2}" y="${this.copy(this.pointerPosition.y) + i.diffY + i.fontSize * b - t.rectHeight / 2}">
${e[b]}
</tspan>`);
t.content += `
${this.computeTextElement(i, l, i.isBulletTextMode)}
`;
break;
}
});
},
chooseAction(t) {
switch (t.preventDefault(), this.isMouseDown = !0, !0) {
case this.isDrawMode:
this.drawDown();
break;
}
},
chooseMove(t) {
switch (t.preventDefault(), t.target.localName !== "svg" && (this.currentTarget = t.target), !0) {
case (this.isMoveMode && this.isMouseDown):
this.moveDown();
break;
case (this.isResizeMode && this.isMouseDown):
this.resize();
break;
}
},
computeCaretPosition(t) {
switch (!0) {
case t.textAlign === "middle":
return `<path stroke="black" stroke-width="2" d="M${t.x},${t.y - t.fontSize} ${t.x},${t.y - t.fontSize - 15}" /> <path stroke="black" stroke-width="2" d="M${t.x - 3},${t.y - t.fontSize - 5} ${t.x},${t.y - t.fontSize} ${t.x + 3},${t.y - t.fontSize - 5}"/>`;
case t.textAlign === "start":
const i = t.isBulletTextMode ? t.fontSize : 0;
return `<path d="M${t.x - 20 - i},${t.y - t.fontSize / 6} ${t.x - 5 - i},${t.y - t.fontSize / 6}" stroke="black" stroke-width="2" />
<path d="M${t.x - 10 - i},${t.y - t.fontSize / 3} ${t.x - 5 - i},${t.y - t.fontSize / 6} ${t.x - 10 - i},${t.y}" stroke="black" stroke-width="2">`;
case t.textAlign === "end":
return `<path d="M${t.x + 20},${t.y - t.fontSize / 6} ${t.x + 5},${t.y - t.fontSize / 6}" stroke="black" stroke-width="2" />
<path d="M${t.x + 10},${t.y - t.fontSize / 3} ${t.x + 5},${t.y - t.fontSize / 6} ${t.x + 10},${t.y}" stroke="black" stroke-width="2">`;
default:
return "";
}
},
computeTextElement(t, i, o = !1) {
switch (!0) {
case t.textAlign === "start":
return `
<g id="${t.id}">
<rect
id="${t.id}"
style="display:${this.lastSelectedShape && this.lastSelectedShape.id === t.id ? "initial" : "none"};"
x="${t.x}"
y="${t.y - 50}"
height="${t.lines === 0 || t.lines === 1 ? t.fontSize * 4 : t.fontSize * 2 * t.lines}"
width="100"
fill="rgba(0,0,0,0)"
/>
<text
style="user-select:none; height:100px;"
id="${t.id}"
x="${t.x}"
y="${t.y}"
text-anchor="${t.textAlign}"
font-size="${t.fontSize}"
fill="${t.color}"
font-weight="${t.isBold ? "bold" : "normal"}"
font-style="${t.isItalic ? "italic" : "normal"}"
text-decoration="${t.isUnderline ? "underline" : "none"}"
>
${i.join("")}
</text>
${this.showCaret && this.lastSelectedShape && this.lastSelectedShape.id === t.id ? this.computeCaretPosition(t) : ""}
${this.includeDeleteButton(t, o)}
</g>
`;
case t.textAlign === "middle":
return `
<g id="${t.id}">
<rect
id="${t.id}"
style="display:${this.lastSelectedShape && this.lastSelectedShape.id === t.id ? "initial" : "none"};"
x="${t.x - 50}"
y="${t.y - 50}"
height="${t.lines === 0 || t.lines === 1 ? t.fontSize * 4 : t.fontSize * 2 * t.lines}"
width="100"
fill="rgba(0,0,0,0)"
/>
<text
style="user-select:none; height:100px;"
id="${t.id}"
x="${t.x}"
y="${t.y}"
text-anchor="${t.textAlign}"
font-size="${t.fontSize}"
fill="${t.color}"
font-weight="${t.isBold ? "bold" : "normal"}"
font-style="${t.isItalic ? "italic" : "normal"}"
text-decoration="${t.isUnderline ? "underline" : "none"}"
>
${i.join("")}
</text>
${this.showCaret && this.lastSelectedShape && this.lastSelectedShape.id === t.id ? this.computeCaretPosition(t) : ""}
${this.includeDeleteButton(t)}
</g>
`;
case t.textAlign === "end":
return `
<g id="${t.id}">
<rect
id="${t.id}"
style="display:${this.lastSelectedShape && this.lastSelectedShape.id === t.id ? "initial" : "none"};"
x="${t.x - 100}"
y="${t.y - 50}"
height="${t.lines === 0 || t.lines === 1 ? t.fontSize * 4 : t.fontSize * 2 * t.lines}"
width="100"
fill="rgba(0,0,0,0)"
/>
<text
style="user-select:none; height:100px;"
id="${t.id}"
x="${t.x}"
y="${t.y}"
text-anchor="${t.textAlign}"
font-size="${t.fontSize}"
fill="${t.color}"
font-weight="${t.isBold ? "bold" : "normal"}"
font-style="${t.isItalic ? "italic" : "normal"}"
text-decoration="${t.isUnderline ? "underline" : "none"}"
>
${i.join("")}
</text>
${this.showCaret && this.lastSelectedShape && this.lastSelectedShape.id === t.id ? this.computeCaretPosition(t) : ""}
${this.includeDeleteButton(t)}
</g>
`;
default:
return "";
}
},
copy(t) {
return JSON.parse(JSON.stringify(t));
},
clickShape(t) {
const i = t.target.id;
switch (!0) {
case this.isDeleteMode:
this.shapes = [...this.shapes].filter((o) => o.id !== i), this.lastSelectedShape = void 0;
break;
default:
this.lastSelectedShape = this.shapes.find((o) => o.id === i);
break;
}
},
deleteEmptyTextElement() {
!this.lastSelectedShape || !this.lastSelectedShape.id.includes("text") || this.lastSelectedShape.textContent === "" && (this.shapes = this.shapes.filter(
(t) => t.id !== this.lastSelectedShape.id
), this.lastSelectedShape = this.shapes.at(-1));
},
drawUp(t = !1) {
if (!this.activeShape || !this.isDrawing)
return;
this.currentPointer.end = {
x: this.pointerPosition.x,
y: this.pointerPosition.y
};
let i;
this.shapes.length > 0 && this.currentTarget && (i = [...this.shapes].find(
(b) => b.id === this.currentTarget.id
));
let o, h, s;
i && (o = i.x - this.currentPointer.end.x, h = i.y - this.currentPointer.end.y, s = Math.sqrt(o * o + h * h));
let e, l;
t ? (e = Math.max(this.currentPointer.end.x, i.x), l = Math.min(this.currentPointer.end.x, i.x), Math.max(this.currentPointer.end.y, i.y), Math.min(this.currentPointer.end.y, i.y)) : (e = Math.max(this.currentPointer.end.x, this.currentPointer.start.x), l = Math.min(this.currentPointer.end.x, this.currentPointer.start.x), Math.max(this.currentPointer.end.y, this.currentPointer.start.y), Math.min(this.currentPointer.end.y, this.currentPointer.start.y)), this.$nextTick(() => {
switch (!0) {
case this.activeShape === "arrow":
this.shapes.at(-1).endX = this.currentPointer.end.x, this.shapes.at(-1).endY = this.currentPointer.end.y;
break;
case this.activeShape === "circle":
const b = 20;
this.shapes.at(-1).circleRadius = this.isDrawingNewShape ? this.copy(e - l) + b : s + b;
break;
case this.activeShape === "line":
this.shapes.at(
-1
).path += ` ${this.pointerPosition.x} ${this.pointerPosition.y} `;
break;
case ["rect", "group"].includes(this.activeShape):
const I = 20;
this.shapes.at(-1).rectWidth = this.copy(this.currentPointer.end.x - this.shapes.at(-1).x) > 0 ? this.copy(this.currentPointer.end.x - this.shapes.at(-1).x) : I, this.shapes.at(-1).rectHeight = this.copy(this.currentPointer.end.y - this.shapes.at(-1).y) > 0 ? this.copy(this.currentPointer.end.y - this.shapes.at(-1).y) : I;
}
});
},
drawDown() {
if (this.isDrawing = !0, !this.activeShape && !this.isSelectMode || !this.isDrawing)
return;
this.isDrawingNewShape = !0, this.currentPointer.start = {
x: this.pointerPosition.x,
y: this.pointerPosition.y
};
let t = `${this.isSelectMode ? "group" : this.activeShape}_${Math.random() * 1e4}_${Date.now()}`;
switch (!0) {
case this.activeShape === "arrow":
this.shapes.push({
id: t,
x: this.pointerPosition.x,
y: this.pointerPosition.y,
endX: this.pointerPosition.x,
endY: this.pointerPosition.y,
type: this.activeShape,
color: this.copy(this.selectedColor),
strokeWidth: this.copy(Math.abs(this.strokeSize)),
isDash: this.copy(this.isDash)
}), this.lastSelectedShape = this.shapes.at(-1);
break;
case this.activeShape === "circle":
this.shapes.push({
alpha: this.options.circle.filled ? this.colorTransparency : "",
id: t,
color: this.copy(this.selectedColor),
isFilled: this.copy(this.options.circle.filled),
circleRadius: this.copy(this.options.circle.radius),
circleStrokeWidth: this.copy(this.options.circle.strokeWidth),
type: this.activeShape,
x: this.pointerPosition.x,
y: this.pointerPosition.y,
strokeWidth: this.copy(Math.abs(this.strokeSize)),
isDash: this.copy(this.isDash)
}), this.lastSelectedShape = this.shapes.at(-1);
break;
case this.activeShape === "line":
this.shapes.push({
alpha: this.copy(this.colorTransparency),
id: t,
x: this.pointerPosition.x,
y: this.pointerPosition.y,
type: this.activeShape,
color: this.copy(this.selectedColor),
strokeWidth: this.copy(Math.abs(this.strokeSize)),
isDash: this.copy(this.isDash),
path: `${this.pointerPosition.x} ${this.pointerPosition.y}`
}), this.lastSelectedShape = this.shapes.at(-1);
break;
case this.activeShape === "rect":
this.shapes.push({
alpha: this.options.rect.filled ? this.colorTransparency : "",
id: t,
color: this.copy(this.selectedColor),
isFilled: this.copy(this.options.rect.filled),
rectStrokeWidth: this.copy(this.options.rect.strokeWidth),
rectHeight: this.copy(this.options.rect.height),
rectWidth: this.copy(this.options.rect.width),
type: this.activeShape,
x: this.pointerPosition.x,
y: this.pointerPosition.y,
strokeWidth: this.copy(Math.abs(this.strokeSize)),
isDash: this.copy(this.isDash)
}), this.lastSelectedShape = this.shapes.at(-1);
break;
case this.activeShape === "group":
this.shapes.push({
alpha: 1,
id: `group_${Math.random() * 1e4}_${Date.now()}`,
x: this.pointerPosition.x,
y: this.pointerPosition.y,
isFilled: !1,
rectHeight: this.copy(this.options.rect.height),
rectWidth: this.copy(this.options.rect.width),
rectStrokeWidth: 1,
type: "group",
color: "grey",
strokeWidth: 1,
isDash: !0,
content: ""
});
break;
}
if ((this.pointerDownId !== -1 || !this.isDrawing) && (clearInterval(this.pointerDownId), this.pointerDownId = -1), this.pointerDownId === -1 && this.isDrawing) {
this.pointerDownId = setInterval(this.drawUp, 1);
return;
}
},
move(t) {
if (!(!t || !t.id || t.type === "line"))
switch (this.lastSelectedShape = t, !0) {
case t.type === "arrow":
t.x = this.copy(this.pointerPosition.x), t.y = this.copy(this.pointerPosition.y);
break;
case t.type === "circle":
t.x = this.copy(this.pointerPosition.x), t.y = this.copy(this.pointerPosition.y);
break;
case t.type === "group":
this.moveGroup(t);
break;
case t.type === "rect":
t.x = this.copy(this.pointerPosition.x - t.rectWidth / 2), t.y = this.copy(this.pointerPosition.y - t.rectHeight / 2);
break;
case t.type === "text":
const i = Array.from(document.getElementsByTagName("text")).find(
(l) => l.id === t.id
);
if (!i)
return;
const { x: o, y: h, width: s, height: e } = i.getBBox();
t.textAlign === "start" && (t.x = this.copy(this.pointerPosition.x - s / 2)), t.textAlign === "middle" && (t.x = this.copy(this.pointerPosition.x)), t.textAlign === "end" && (t.x = this.copy(this.pointerPosition.x + s / 2)), t.lines > 1 ? t.y = this.copy(this.pointerPosition.y - e / 3) : t.y = this.copy(this.pointerPosition.y + t.fontSize / 2);
break;
}
},
moveDown() {
if (!this.currentTarget || !this.currentTarget.id)
return;
const t = this.currentTarget.id, i = this.shapes.find((o) => o.id === t);
this.shapes = this.shapes.filter((o) => o.id !== t), this.shapes.push(i), this.pointerDownId === -1 && t && this.move(i);
},
print() {
this.isPrinting = !0, this.isDeleteMode = !1, this.isMoveMode = !1, this.isResizeMode = !1, this.isTextMode = !1, this.isWriting = !1, this.isSelectMode = !1, this.activeShape = void 0, this.showCaret = !1, this.$nextTick(() => {
const t = this.$refs.drawSvgContainer, i = {
height: 851.89,
width: 595.28
};
this.walkTheDOM(t, (o) => {
o && o.nodeType === 1 && (o.setAttribute("font-family", "Helvetica"), o.style.fontFamily = "Helvetica", o.replaceWith(o));
}), k(t).then((o) => {
const h = o.width, s = o.height, e = h / i.width * i.height;
let l = s, b = 0;
const I = i.width, g = 582.28 / h * s, S = o.toDataURL("image/png", 1), f = new p("", "pt", "a4");
if (l < e)
f.addImage(S, "PNG", 0, 0, I, g, "", "FAST");
else
for (; l > 0; )
f.addImage(
S,
"PNG",
0,
b,
I,
g,
"",
"FAST"
), l -= e, b -= i.height - 24, l > 0 && f.addPage();
f.save(`${(/* @__PURE__ */ new Date()).toLocaleDateString()}_annotations.pdf`);
}).finally(() => {
this.isPrinting = !1, this.walkTheDOM(t, (o) => {
o && o.nodeType === 1 && (o.setAttribute("font-family", this.FINAL_CONFIG.style.fontFamily), o.style.fontFamily = this.FINAL_CONFIG.style.fontFamily, o.replaceWith(o));
});
});
});
},
resetDraw() {
this.isDrawing = !1, this.isMouseDown = !1, this.pointerDownId = -1, this.isSelectMode && this.groupShapes(), clearInterval(this.pointerDownId);
},
resize() {
this.isDrawingNewShape = !1;
const t = this.currentTarget.id;
if (!t)
return;
this.isDrawing = !0;
const i = this.shapes.find((o) => o.id === t);
this.activeShape = i.type, this.shapes = this.shapes.filter((o) => o.id !== t), this.shapes.push(i), this.drawUp(!0);
},
setFillOfSelectedRect() {
!this.lastSelectedShape || !this.lastSelectedShape.id.includes("rect") || (this.lastSelectedShape.isFilled = !this.lastSelectedShape.isFilled);
},
setFillOfSelectedCircle() {
!this.lastSelectedShape || !this.lastSelectedShape.id.includes("circle") || (this.lastSelectedShape.isFilled = !this.lastSelectedShape.isFilled);
},
setColorOfSelectedShape() {
this.lastSelectedShape && (this.lastSelectedShape.color = this.copy(this.selectedColor), !["arrow", "text"].includes(this.lastSelectedShape.id) && (this.lastSelectedShape.alpha = this.copy(this.colorTransparency)));
},
setSelectedShapeToDash() {
!this.lastSelectedShape || this.lastSelectedShape.type === "text" || (this.lastSelectedShape.isDash = this.copy(this.isDash));
},
setTransparencyOfSelectedShape() {
!this.lastSelectedShape || ["arrow", "text"].includes(this.lastSelectedShape.id) || (this.lastSelectedShape.alpha = this.copy(this.colorTransparency));
},
setStrokeWidthOfSelectedShape() {
!this.lastSelectedShape || !["arrow", "circle", "rect", "line"].includes(this.lastSelectedShape.type) || (this.lastSelectedShape.strokeWidth = this.copy(Math.abs(this.strokeSize)));
},
setCurrentStyleOfSelectedText() {
!this.lastSelectedShape || this.lastSelectedShape.type !== "text" || (this.lastSelectedShape.isBold = this.copy(this.isBold), this.lastSelectedShape.isItalic = this.copy(this.isItalic), this.lastSelectedShape.isUnderline = this.copy(this.isUnderline), this.lastSelectedShape.fontSize = this.copy(this.textFont), this.lastSelectedShape.isBulletTextMode = this.copy(this.isBulletTextMode));
},
setPointer(t) {
t.preventDefault();
const o = this.$refs.mainSvg.getBoundingClientRect();
let h, s;
t.touches && t.touches.length > 0 ? (h = t.touches[0].clientX, s = t.touches[0].clientY) : (h = t.clientX, s = t.clientY), this.pointerPosition.x = (h - o.left) / o.width * this.svgWidth, this.pointerPosition.y = (s - o.top) / o.height * this.svgHeight;
},
setShapeTo(t) {
if (this.showCaret = !1, this.deleteEmptyTextElement(), t === this.activeShape) {
this.activeShape = void 0, this.isDrawMode = !1;
return;
}
this.isDrawMode = !0, this.isDeleteMode = !1, this.isMoveMode = !1, this.isResizeMode = !1, this.isTextMode = !1, this.activeShape = t;
},
toggleSummary() {
this.isSummaryOpen = !this.isSummaryOpen, this.isSummaryOpen || (this.isMoveMode = !1, this.isResizeMode = !1, this.isTextMode = !1, this.isWriting = !1, this.activeShape = void 0, this.showCaret = !1, this.isDeleteMode = !1, this.isWriting = !1), this.$emit("toggleOpenState", { isOpen: this.isSummaryOpen });
},
walkTheDOM(t, i) {
for (i(t), t = t.firstChild; t; )
this.walkTheDOM(t, i), t = t.nextSibling;
},
save() {
this.$emit("saveAnnotations", {
shapes: this.shapes,
lastSelectedShape: this.lastSelectedShape
});
}
}
}, T = { class: "vue-ui-annotator" }, R = { "data-html2canvas-ignore": "" }, D = {
class: "tool-selection",
style: { "margin-top": "24px" }
}, z = ["disabled"], H = ["disabled"], W = ["disabled"], P = ["disabled"], B = {
style: { width: "80%" },
viewBox: "0 0 24 24"
}, V = ["disabled"], E = {
style: { width: "80%" },
viewBox: "0 0 24 24"