canva-pptx 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1147 -0
- package/package.json +63 -0
- package/src/AdvancedComponents/accordionList.js +39 -0
- package/src/AdvancedComponents/calendarGrid.js +50 -0
- package/src/AdvancedComponents/certificateFrame.js +35 -0
- package/src/AdvancedComponents/deviceMockup.js +59 -0
- package/src/AdvancedComponents/featureGrid.js +70 -0
- package/src/AdvancedComponents/funnelDiagram.js +49 -0
- package/src/AdvancedComponents/ganttChart.js +35 -0
- package/src/AdvancedComponents/invoiceTable.js +32 -0
- package/src/AdvancedComponents/matrixGrid.js +43 -0
- package/src/AdvancedComponents/mindMap.js +43 -0
- package/src/AdvancedComponents/orgChart.js +76 -0
- package/src/AdvancedComponents/personaCard.js +44 -0
- package/src/AdvancedComponents/processCycle.js +47 -0
- package/src/AdvancedComponents/pyramidHierarchy.js +38 -0
- package/src/AdvancedComponents/saasFeatureBlock.js +38 -0
- package/src/AdvancedComponents/swotMatrix.js +55 -0
- package/src/AdvancedComponents/teamMemberProfile.js +75 -0
- package/src/AdvancedComponents/trafficLight.js +35 -0
- package/src/AdvancedComponents/vennDiagram.js +35 -0
- package/src/AdvancedComponents/winLossChart.js +43 -0
- package/src/components/StatMetric.js +43 -0
- package/src/components/alertBox.js +46 -0
- package/src/components/avatarGroup.js +56 -0
- package/src/components/badge.js +40 -0
- package/src/components/breadcrumbNav.js +31 -0
- package/src/components/browserWindow.js +86 -0
- package/src/components/brushStroke.js +23 -0
- package/src/components/callToAction.js +27 -0
- package/src/components/card.js +64 -0
- package/src/components/chart.js +19 -0
- package/src/components/codeBlock.js +40 -0
- package/src/components/codeDiff.js +51 -0
- package/src/components/comparisonTable.js +43 -0
- package/src/components/cornerAccent.js +32 -0
- package/src/components/dotPattern.js +30 -0
- package/src/components/geometricConfetti.js +34 -0
- package/src/components/gradientMesh.js +32 -0
- package/src/components/iconList.js +43 -0
- package/src/components/image.js +11 -0
- package/src/components/kanbanColumn.js +38 -0
- package/src/components/link.js +23 -0
- package/src/components/organicBlob.js +45 -0
- package/src/components/pricingColumn.js +53 -0
- package/src/components/progressBar.js +55 -0
- package/src/components/ratingStars.js +25 -0
- package/src/components/shape.js +12 -0
- package/src/components/slide.js +5 -0
- package/src/components/socialBar.js +59 -0
- package/src/components/squiggleLine.js +26 -0
- package/src/components/stepProcess.js +39 -0
- package/src/components/table.js +23 -0
- package/src/components/tagCloud.js +39 -0
- package/src/components/testimonialCard.js +54 -0
- package/src/components/text.js +14 -0
- package/src/components/theme.js +7 -0
- package/src/components/timeline.js +73 -0
- package/src/components/waveDecoration.js +35 -0
- package/src/core/PPTManager.js +17 -0
- package/src/index.js +225 -0
- package/src/layout/bento.js +47 -0
- package/src/layout/checkerboard.js +36 -0
- package/src/layout/filmStrip.js +29 -0
- package/src/layout/gallery.js +50 -0
- package/src/layout/grid.js +37 -0
- package/src/layout/hero.js +30 -0
- package/src/layout/magazine.js +39 -0
- package/src/layout/radial.js +34 -0
- package/src/layout/sidebar.js +26 -0
- package/src/layout/splitScreen.js +36 -0
- package/src/layout/zPattern.js +29 -0
- package/src/system/animation.js +36 -0
- package/src/system/contrastChecker.js +35 -0
- package/src/system/dataAdapter.js +36 -0
- package/src/system/layoutDebugger.js +40 -0
- package/src/system/markdownEngine.js +45 -0
- package/src/system/masterOverlay.js +50 -0
- package/src/system/sectionDivider.js +41 -0
- package/src/system/smartIcon.js +33 -0
- package/src/system/smartText.js +44 -0
- package/src/system/speakerNotes.js +12 -0
- package/src/system/syntaxHighlighter.js +71 -0
- package/src/system/themeGenerator.js +25 -0
- package/src/system/tocGenerator.js +68 -0
- package/src/system/watermark.js +26 -0
- package/src/themes/index.js +93 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card component
|
|
3
|
+
* Supports any pptx.ShapeType
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
function addCard(slide, options = {}) {
|
|
7
|
+
const {
|
|
8
|
+
x,
|
|
9
|
+
y,
|
|
10
|
+
w,
|
|
11
|
+
h,
|
|
12
|
+
background = "FFFFFF",
|
|
13
|
+
border = "DDDDDD",
|
|
14
|
+
padding = 0.3,
|
|
15
|
+
title,
|
|
16
|
+
body,
|
|
17
|
+
theme,
|
|
18
|
+
shapeType, // REQUIRED
|
|
19
|
+
} = options;
|
|
20
|
+
|
|
21
|
+
if (!shapeType) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
"addCard: shapeType is required (e.g. pptx.ShapeType.rect)"
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Card shape
|
|
28
|
+
slide.addShape(shapeType, {
|
|
29
|
+
x,
|
|
30
|
+
y,
|
|
31
|
+
w,
|
|
32
|
+
h,
|
|
33
|
+
fill: background,
|
|
34
|
+
line: { color: border },
|
|
35
|
+
rounding: shapeType === "roundRect" ? 0.15 : undefined,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Title
|
|
39
|
+
if (title) {
|
|
40
|
+
slide.addText(title, {
|
|
41
|
+
x: x + padding,
|
|
42
|
+
y: y + padding,
|
|
43
|
+
w: w - padding * 2,
|
|
44
|
+
fontSize: theme?.heading?.fontSize || 16,
|
|
45
|
+
bold: true,
|
|
46
|
+
color: theme?.heading?.color || "111111",
|
|
47
|
+
align: "center",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Body
|
|
52
|
+
if (body) {
|
|
53
|
+
slide.addText(body, {
|
|
54
|
+
x: x + padding,
|
|
55
|
+
y: y + padding + 0.6,
|
|
56
|
+
w: w - padding * 2,
|
|
57
|
+
fontSize: theme?.body?.fontSize || 12,
|
|
58
|
+
color: theme?.body?.color || "444444",
|
|
59
|
+
align: "center",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = { addCard };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
function addChart(slide, chartType, data, options = {}) {
|
|
2
|
+
slide.addChart(chartType, data, {
|
|
3
|
+
x: options.x ?? 1,
|
|
4
|
+
y: options.y ?? 1,
|
|
5
|
+
w: options.w ?? 6,
|
|
6
|
+
h: options.h ?? 4,
|
|
7
|
+
|
|
8
|
+
title: options.title,
|
|
9
|
+
showLegend: options.showLegend ?? true,
|
|
10
|
+
|
|
11
|
+
// INTERACTIVITY (PowerPoint-native)
|
|
12
|
+
chartColors: options.colors,
|
|
13
|
+
animation: options.animation ?? "fade",
|
|
14
|
+
showValue: options.showValue ?? true,
|
|
15
|
+
showPercent: options.showPercent ?? false,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = { addChart };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders a code snippet box
|
|
3
|
+
*/
|
|
4
|
+
function addCodeBlock(slide, code, options = {}) {
|
|
5
|
+
const {
|
|
6
|
+
x = 1,
|
|
7
|
+
y = 1,
|
|
8
|
+
w = 6,
|
|
9
|
+
h = 3,
|
|
10
|
+
fontSize = 11,
|
|
11
|
+
background = "1E1E1E", // Dark VS Code style
|
|
12
|
+
color = "D4D4D4",
|
|
13
|
+
} = options;
|
|
14
|
+
|
|
15
|
+
// Container
|
|
16
|
+
slide.addShape("rect", {
|
|
17
|
+
x,
|
|
18
|
+
y,
|
|
19
|
+
w,
|
|
20
|
+
h,
|
|
21
|
+
fill: background,
|
|
22
|
+
line: { color: "333333" },
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Code Text (Monospaced font usually requires a specific font name installed,
|
|
26
|
+
// but standard fonts work for general purpose)
|
|
27
|
+
slide.addText(code, {
|
|
28
|
+
x: x + 0.2,
|
|
29
|
+
y: y + 0.2,
|
|
30
|
+
w: w - 0.4,
|
|
31
|
+
h: h - 0.4,
|
|
32
|
+
fontSize: fontSize,
|
|
33
|
+
color: color,
|
|
34
|
+
fontFace: "Courier New", // Standard monospaced font
|
|
35
|
+
align: "left",
|
|
36
|
+
valign: "top",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { addCodeBlock };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders a code block that highlights diffs.
|
|
3
|
+
* Lines starting with "+" are green.
|
|
4
|
+
* Lines starting with "-" are red.
|
|
5
|
+
*/
|
|
6
|
+
function addCodeDiff(slide, diffText, options = {}) {
|
|
7
|
+
const {
|
|
8
|
+
x = 1, y = 1, w = 8, h = 4,
|
|
9
|
+
fontSize = 12,
|
|
10
|
+
background = "1E1E1E"
|
|
11
|
+
} = options;
|
|
12
|
+
|
|
13
|
+
// Background
|
|
14
|
+
slide.addShape("rect", { x, y, w, h, fill: background });
|
|
15
|
+
|
|
16
|
+
const lines = diffText.split("\n");
|
|
17
|
+
let currentY = y + 0.2;
|
|
18
|
+
const lineHeight = fontSize * 0.0035;
|
|
19
|
+
|
|
20
|
+
lines.forEach(line => {
|
|
21
|
+
let color = "D4D4D4"; // Default Grey
|
|
22
|
+
let bgLine = null;
|
|
23
|
+
|
|
24
|
+
if (line.startsWith("+")) {
|
|
25
|
+
color = "4CAF50"; // Green text
|
|
26
|
+
// Optional: Add faint green background strip for the line
|
|
27
|
+
// bgLine = "0f291e";
|
|
28
|
+
} else if (line.startsWith("-")) {
|
|
29
|
+
color = "F44336"; // Red text
|
|
30
|
+
// bgLine = "3e1616";
|
|
31
|
+
} else if (line.startsWith("@@")) {
|
|
32
|
+
color = "569CD6"; // Blue for chunk headers
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Render Text
|
|
36
|
+
slide.addText(line, {
|
|
37
|
+
x: x + 0.2,
|
|
38
|
+
y: currentY,
|
|
39
|
+
w: w - 0.4,
|
|
40
|
+
h: lineHeight,
|
|
41
|
+
fontFace: "Courier New",
|
|
42
|
+
fontSize: fontSize,
|
|
43
|
+
color: color,
|
|
44
|
+
align: "left"
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
currentY += 0.25;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = { addCodeDiff };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
function addComparisonTable(slide, leftData, rightData, options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
x = 1, y = 1, w = 8, h = 4,
|
|
4
|
+
leftColor = "E8F5E9", rightColor = "FFEBEE"
|
|
5
|
+
} = options;
|
|
6
|
+
|
|
7
|
+
const colW = w / 2;
|
|
8
|
+
|
|
9
|
+
// Backgrounds
|
|
10
|
+
slide.addShape("rect", { x, y, w: colW, h, fill: leftColor });
|
|
11
|
+
slide.addShape("rect", { x: x + colW, y, w: colW, h, fill: rightColor });
|
|
12
|
+
|
|
13
|
+
// Headers
|
|
14
|
+
slide.addText(leftData.title, {
|
|
15
|
+
x, y, w: colW, h: 0.5,
|
|
16
|
+
bold: true, align: "center", fontSize: 16
|
|
17
|
+
});
|
|
18
|
+
slide.addText(rightData.title, {
|
|
19
|
+
x: x + colW, y, w: colW, h: 0.5,
|
|
20
|
+
bold: true, align: "center", fontSize: 16
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Lists
|
|
24
|
+
const listOpts = { fontSize: 12, bullet: true, color: "333333" };
|
|
25
|
+
|
|
26
|
+
// Left Items
|
|
27
|
+
leftData.items.forEach((item, i) => {
|
|
28
|
+
slide.addText(item, {
|
|
29
|
+
x: x + 0.2, y: y + 0.6 + (i*0.4), w: colW - 0.4, h: 0.4,
|
|
30
|
+
...listOpts
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Right Items
|
|
35
|
+
rightData.items.forEach((item, i) => {
|
|
36
|
+
slide.addText(item, {
|
|
37
|
+
x: x + colW + 0.2, y: y + 0.6 + (i*0.4), w: colW - 0.4, h: 0.4,
|
|
38
|
+
...listOpts
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { addComparisonTable };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
function addCornerAccent(slide, corner = "top-right", options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
size = 2,
|
|
4
|
+
color = "FFC107" // Amber
|
|
5
|
+
} = options;
|
|
6
|
+
|
|
7
|
+
let x, y, rotate;
|
|
8
|
+
const slideW = 10; // Default width
|
|
9
|
+
const slideH = 5.63; // Default height
|
|
10
|
+
|
|
11
|
+
switch (corner) {
|
|
12
|
+
case "top-left": x = -size/2; y = -size/2; rotate = 180; break;
|
|
13
|
+
case "bottom-left": x = -size/2; y = slideH - size/2; rotate = 270; break;
|
|
14
|
+
case "bottom-right": x = slideW - size/2; y = slideH - size/2; rotate = 0; break;
|
|
15
|
+
case "top-right": default: x = slideW - size/2; y = -size/2; rotate = 90; break;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// A quarter circle (arc) or a triangle in the corner.
|
|
19
|
+
// Using a rotated Triangle is very modern.
|
|
20
|
+
|
|
21
|
+
slide.addShape("rtTriangle", { // Right Triangle
|
|
22
|
+
x: x,
|
|
23
|
+
y: y,
|
|
24
|
+
w: size,
|
|
25
|
+
h: size,
|
|
26
|
+
fill: color,
|
|
27
|
+
rotate: rotate,
|
|
28
|
+
line: { color: undefined }
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { addCornerAccent };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
function addDotPattern(slide, options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
x = 1,
|
|
4
|
+
y = 1,
|
|
5
|
+
w = 3,
|
|
6
|
+
h = 3,
|
|
7
|
+
color = "CCCCCC",
|
|
8
|
+
rows = 6,
|
|
9
|
+
cols = 6,
|
|
10
|
+
size = 0.05
|
|
11
|
+
} = options;
|
|
12
|
+
|
|
13
|
+
const gapX = (w - size) / (cols - 1);
|
|
14
|
+
const gapY = (h - size) / (rows - 1);
|
|
15
|
+
|
|
16
|
+
for (let r = 0; r < rows; r++) {
|
|
17
|
+
for (let c = 0; c < cols; c++) {
|
|
18
|
+
slide.addShape("oval", {
|
|
19
|
+
x: x + c * gapX,
|
|
20
|
+
y: y + r * gapY,
|
|
21
|
+
w: size,
|
|
22
|
+
h: size,
|
|
23
|
+
fill: color,
|
|
24
|
+
line: { color: undefined }
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { addDotPattern };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
function addGeometricConfetti(slide, options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
x = 0,
|
|
4
|
+
y = 0,
|
|
5
|
+
w = 10,
|
|
6
|
+
h = 5.63,
|
|
7
|
+
count = 10,
|
|
8
|
+
colors = ["FF5722", "00BCD4", "FFC107"]
|
|
9
|
+
} = options;
|
|
10
|
+
|
|
11
|
+
const shapes = ["triangle", "oval", "rect", "cross"];
|
|
12
|
+
|
|
13
|
+
for (let i = 0; i < count; i++) {
|
|
14
|
+
// Random positions within the bounding box
|
|
15
|
+
const randX = x + Math.random() * w;
|
|
16
|
+
const randY = y + Math.random() * h;
|
|
17
|
+
const randSize = 0.1 + Math.random() * 0.2; // Small size
|
|
18
|
+
const randShape = shapes[Math.floor(Math.random() * shapes.length)];
|
|
19
|
+
const randColor = colors[Math.floor(Math.random() * colors.length)];
|
|
20
|
+
const randRot = Math.random() * 360;
|
|
21
|
+
|
|
22
|
+
slide.addShape(randShape, {
|
|
23
|
+
x: randX,
|
|
24
|
+
y: randY,
|
|
25
|
+
w: randSize,
|
|
26
|
+
h: randSize,
|
|
27
|
+
fill: randColor,
|
|
28
|
+
rotate: randRot,
|
|
29
|
+
line: { color: undefined }
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { addGeometricConfetti };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
function addGradientMesh(slide, options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
x = 0,
|
|
4
|
+
y = 0,
|
|
5
|
+
w = 5,
|
|
6
|
+
h = 5,
|
|
7
|
+
color = "007AFF",
|
|
8
|
+
opacity = 50
|
|
9
|
+
} = options;
|
|
10
|
+
|
|
11
|
+
// PowerPoint JS libraries often handle gradients via specific fill objects.
|
|
12
|
+
// We simulate a mesh glow using a radial gradient if supported,
|
|
13
|
+
// or a highly transparent soft circle.
|
|
14
|
+
|
|
15
|
+
slide.addShape("oval", {
|
|
16
|
+
x: x,
|
|
17
|
+
y: y,
|
|
18
|
+
w: w,
|
|
19
|
+
h: h,
|
|
20
|
+
fill: {
|
|
21
|
+
type: "gradient",
|
|
22
|
+
gradientType: "radial",
|
|
23
|
+
stops: [
|
|
24
|
+
{ position: 0, color: color, alpha: (100-opacity)/100 }, // Center
|
|
25
|
+
{ position: 1, color: "FFFFFF", alpha: 0 } // Edge (fade out)
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
line: { color: undefined }
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { addGradientMesh };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
function addIconList(slide, items = [], options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
x = 1,
|
|
4
|
+
y = 1,
|
|
5
|
+
w = 5,
|
|
6
|
+
itemHeight = 0.5,
|
|
7
|
+
gap = 0.1,
|
|
8
|
+
iconColor = "007AFF",
|
|
9
|
+
textColor = "333333"
|
|
10
|
+
} = options;
|
|
11
|
+
|
|
12
|
+
items.forEach((text, i) => {
|
|
13
|
+
const curY = y + i * (itemHeight + gap);
|
|
14
|
+
|
|
15
|
+
// Icon (Checkmark style shape)
|
|
16
|
+
slide.addShape("oval", {
|
|
17
|
+
x: x,
|
|
18
|
+
y: curY + 0.1,
|
|
19
|
+
w: 0.25,
|
|
20
|
+
h: 0.25,
|
|
21
|
+
fill: iconColor
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Checkmark inside
|
|
25
|
+
slide.addText("✓", {
|
|
26
|
+
x: x, y: curY + 0.1, w: 0.25, h: 0.25,
|
|
27
|
+
color: "FFFFFF", fontSize: 10, align: "center", valign: "middle"
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Text
|
|
31
|
+
slide.addText(text, {
|
|
32
|
+
x: x + 0.4,
|
|
33
|
+
y: curY,
|
|
34
|
+
w: w - 0.4,
|
|
35
|
+
h: itemHeight,
|
|
36
|
+
fontSize: 14,
|
|
37
|
+
color: textColor,
|
|
38
|
+
valign: "middle"
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { addIconList };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
function addKanbanColumn(slide, title, cards = [], options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
x = 1, y = 1, w = 3, h = 5,
|
|
4
|
+
bgColor = "F4F5F7"
|
|
5
|
+
} = options;
|
|
6
|
+
|
|
7
|
+
// Background
|
|
8
|
+
slide.addShape("rect", {
|
|
9
|
+
x, y, w, h,
|
|
10
|
+
fill: bgColor,
|
|
11
|
+
rounding: 0.1 // slight rounded corners for column
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// Title
|
|
15
|
+
slide.addText(title, {
|
|
16
|
+
x: x + 0.1, y: y + 0.1, w: w - 0.2, h: 0.4,
|
|
17
|
+
bold: true, fontSize: 12, color: "172B4D"
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Cards
|
|
21
|
+
let cardY = y + 0.6;
|
|
22
|
+
cards.forEach(cardText => {
|
|
23
|
+
slide.addShape("rect", {
|
|
24
|
+
x: x + 0.1, y: cardY, w: w - 0.2, h: 0.8,
|
|
25
|
+
fill: "FFFFFF",
|
|
26
|
+
line: { color: "DFE1E6" } // Border
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
slide.addText(cardText, {
|
|
30
|
+
x: x + 0.2, y: cardY, w: w - 0.4, h: 0.8,
|
|
31
|
+
fontSize: 10, color: "333333", valign: "middle"
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
cardY += 0.9;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = { addKanbanColumn };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
function addLink(slide, text, url, options = {}) {
|
|
2
|
+
slide.addText(
|
|
3
|
+
[
|
|
4
|
+
{
|
|
5
|
+
text,
|
|
6
|
+
options: {
|
|
7
|
+
hyperlink: { url },
|
|
8
|
+
color: "0000FF",
|
|
9
|
+
underline: true,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
{
|
|
14
|
+
x: options.x ?? 1,
|
|
15
|
+
y: options.y ?? 1,
|
|
16
|
+
w: options.w ?? 4,
|
|
17
|
+
h: options.h ?? 1,
|
|
18
|
+
fontSize: options.fontSize ?? 14,
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { addLink };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
function addOrganicBlob(slide, options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
x = 1,
|
|
4
|
+
y = 1,
|
|
5
|
+
w = 4,
|
|
6
|
+
h = 4,
|
|
7
|
+
color = "F0F4FF", // Very light blue
|
|
8
|
+
opacity = 100 // 0-100 (pptxgenjs usually handles transparency via alpha or hex)
|
|
9
|
+
} = options;
|
|
10
|
+
|
|
11
|
+
// To simulate a "blob", we create a cluster of 3 rotated ovals
|
|
12
|
+
// anchored around the center point.
|
|
13
|
+
|
|
14
|
+
// Blob Part 1 (Main body)
|
|
15
|
+
slide.addShape("oval", {
|
|
16
|
+
x: x,
|
|
17
|
+
y: y,
|
|
18
|
+
w: w,
|
|
19
|
+
h: h * 0.8,
|
|
20
|
+
fill: { color: color, transparency: 100 - opacity },
|
|
21
|
+
line: { color: undefined } // No border
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Blob Part 2 (rotated stretch)
|
|
25
|
+
slide.addShape("oval", {
|
|
26
|
+
x: x - w * 0.1,
|
|
27
|
+
y: y + h * 0.1,
|
|
28
|
+
w: w * 0.9,
|
|
29
|
+
h: h * 0.9,
|
|
30
|
+
rotate: 45,
|
|
31
|
+
fill: { color: color, transparency: 100 - opacity }
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Blob Part 3 (smoothing clump)
|
|
35
|
+
slide.addShape("oval", {
|
|
36
|
+
x: x + w * 0.2,
|
|
37
|
+
y: y - h * 0.1,
|
|
38
|
+
w: w * 0.7,
|
|
39
|
+
h: h * 0.7,
|
|
40
|
+
rotate: -30,
|
|
41
|
+
fill: { color: color, transparency: 100 - opacity }
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = { addOrganicBlob };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
function addPricingColumn(slide, plan, options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
x = 1, y = 1, w = 2.5, h = 4,
|
|
4
|
+
headerColor = "333333",
|
|
5
|
+
priceColor = "007AFF"
|
|
6
|
+
} = options;
|
|
7
|
+
|
|
8
|
+
// Container Border
|
|
9
|
+
slide.addShape("rect", {
|
|
10
|
+
x, y, w, h,
|
|
11
|
+
line: { color: "DDDDDD" },
|
|
12
|
+
fill: "FFFFFF"
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Header
|
|
16
|
+
slide.addShape("rect", {
|
|
17
|
+
x, y, w, h: 0.8,
|
|
18
|
+
fill: headerColor
|
|
19
|
+
});
|
|
20
|
+
slide.addText(plan.name, {
|
|
21
|
+
x, y, w, h: 0.8,
|
|
22
|
+
color: "FFFFFF", bold: true, align: "center", valign: "middle", fontSize: 14
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Price
|
|
26
|
+
slide.addText(plan.price, {
|
|
27
|
+
x, y: y + 0.9, w, h: 0.6,
|
|
28
|
+
color: priceColor, bold: true, align: "center", fontSize: 24
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Features
|
|
32
|
+
const startY = y + 1.6;
|
|
33
|
+
if (plan.features) {
|
|
34
|
+
plan.features.forEach((feat, i) => {
|
|
35
|
+
slide.addText(feat, {
|
|
36
|
+
x: x + 0.2, y: startY + (i * 0.4), w: w - 0.4, h: 0.4,
|
|
37
|
+
fontSize: 10, align: "center", color: "666666"
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Button Visual
|
|
43
|
+
slide.addShape("roundRect", {
|
|
44
|
+
x: x + 0.4, y: y + h - 0.7, w: w - 0.8, h: 0.4,
|
|
45
|
+
fill: priceColor, rounding: 0.5
|
|
46
|
+
});
|
|
47
|
+
slide.addText("Select", {
|
|
48
|
+
x: x + 0.4, y: y + h - 0.7, w: w - 0.8, h: 0.4,
|
|
49
|
+
color: "FFFFFF", fontSize: 10, bold: true, align: "center", valign: "middle"
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = { addPricingColumn };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders a progress bar (track + fill)
|
|
3
|
+
*/
|
|
4
|
+
function addProgressBar(slide, options = {}) {
|
|
5
|
+
const {
|
|
6
|
+
x = 1,
|
|
7
|
+
y = 1,
|
|
8
|
+
w = 5,
|
|
9
|
+
h = 0.3,
|
|
10
|
+
percent = 0.5, // 0.0 to 1.0
|
|
11
|
+
color = "007AFF",
|
|
12
|
+
trackColor = "E5E5EA",
|
|
13
|
+
showLabel = true,
|
|
14
|
+
} = options;
|
|
15
|
+
|
|
16
|
+
// 1. Draw Track (Background)
|
|
17
|
+
slide.addShape("rect", {
|
|
18
|
+
x,
|
|
19
|
+
y,
|
|
20
|
+
w,
|
|
21
|
+
h,
|
|
22
|
+
fill: trackColor,
|
|
23
|
+
line: { color: trackColor },
|
|
24
|
+
rounding: h / 2, // Pill shape
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// 2. Draw Fill (Foreground)
|
|
28
|
+
if (percent > 0) {
|
|
29
|
+
slide.addShape("rect", {
|
|
30
|
+
x,
|
|
31
|
+
y,
|
|
32
|
+
w: w * percent,
|
|
33
|
+
h,
|
|
34
|
+
fill: color,
|
|
35
|
+
line: { color: color }, // Hide border
|
|
36
|
+
rounding: h / 2,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 3. Optional Label (e.g., "50%")
|
|
41
|
+
if (showLabel) {
|
|
42
|
+
slide.addText(`${Math.round(percent * 100)}%`, {
|
|
43
|
+
x: x + w + 0.1,
|
|
44
|
+
y: y,
|
|
45
|
+
w: 1,
|
|
46
|
+
h: h,
|
|
47
|
+
fontSize: 10,
|
|
48
|
+
color: "666666",
|
|
49
|
+
align: "left",
|
|
50
|
+
valign: "middle",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { addProgressBar };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
function addRatingStars(slide, rating = 0, options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
x = 1, y = 1, w = 2, h = 0.4,
|
|
4
|
+
max = 5,
|
|
5
|
+
fillColor = "FFD700", // Gold
|
|
6
|
+
emptyColor = "E0E0E0"
|
|
7
|
+
} = options;
|
|
8
|
+
|
|
9
|
+
const starSize = h;
|
|
10
|
+
const gap = 0.1;
|
|
11
|
+
|
|
12
|
+
for (let i = 1; i <= max; i++) {
|
|
13
|
+
const isFilled = i <= rating;
|
|
14
|
+
const curX = x + (i - 1) * (starSize + gap);
|
|
15
|
+
|
|
16
|
+
// Using text "★" is safer than shapes for compatibility
|
|
17
|
+
slide.addText("★", {
|
|
18
|
+
x: curX, y, w: starSize, h: starSize,
|
|
19
|
+
color: isFilled ? fillColor : emptyColor,
|
|
20
|
+
fontSize: starSize * 40 // approximate scaling
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = { addRatingStars };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
function addShape(slide, shapeType, options = {}) {
|
|
2
|
+
slide.addShape(shapeType, {
|
|
3
|
+
x: options.x ?? 1,
|
|
4
|
+
y: options.y ?? 1,
|
|
5
|
+
w: options.w ?? 2,
|
|
6
|
+
h: options.h ?? 2,
|
|
7
|
+
fill: options.fill ?? "FFFFFF",
|
|
8
|
+
line: options.line ?? { color: "000000" },
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = { addShape };
|