Skip to content

Commit 1d49588

Browse files
committed
Add new slide masters for enhanced presentation layouts
This commit adds six new slide master templates to expand presentation capabilities: - Table: Title with formatted table including styled headers and data rows - TwoColumn: Newspaper-style two-column text layout - ThreeColumn: Three equal columns for balanced content distribution - Quote: Large pull quote with optional attribution for emphasis - Agenda: Numbered table of contents/agenda list - ThreeUp: Three content boxes (teasers) in a single row All masters implement the DynamicallyCreatable interface with proper data schemas, descriptions, and example data. They follow existing patterns using TextBox components, WithSlideTitle trait, and proper spacing/positioning.
1 parent 6b2b537 commit 1d49588

File tree

7 files changed

+686
-0
lines changed

7 files changed

+686
-0
lines changed

src/LaravelPptServiceProvider.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use BernskioldMedia\LaravelPpt\Commands\CreateNewSlideDeckCommand;
77
use BernskioldMedia\LaravelPpt\Commands\GenerateSamplePresentationCommand;
88
use BernskioldMedia\LaravelPpt\Registries\SlideMasters;
9+
use BernskioldMedia\LaravelPpt\SlideMasters\Agenda;
910
use BernskioldMedia\LaravelPpt\SlideMasters\Blank;
1011
use BernskioldMedia\LaravelPpt\SlideMasters\BlankWithTitle;
1112
use BernskioldMedia\LaravelPpt\SlideMasters\BlankWithTitleSubtitle;
@@ -16,10 +17,15 @@
1617
use BernskioldMedia\LaravelPpt\SlideMasters\ChartTitle;
1718
use BernskioldMedia\LaravelPpt\SlideMasters\ChartTitles;
1819
use BernskioldMedia\LaravelPpt\SlideMasters\FourUp;
20+
use BernskioldMedia\LaravelPpt\SlideMasters\Quote;
1921
use BernskioldMedia\LaravelPpt\SlideMasters\SixUp;
22+
use BernskioldMedia\LaravelPpt\SlideMasters\Table;
2023
use BernskioldMedia\LaravelPpt\SlideMasters\Text;
24+
use BernskioldMedia\LaravelPpt\SlideMasters\ThreeColumn;
25+
use BernskioldMedia\LaravelPpt\SlideMasters\ThreeUp;
2126
use BernskioldMedia\LaravelPpt\SlideMasters\Title;
2227
use BernskioldMedia\LaravelPpt\SlideMasters\TitleSubtitle;
28+
use BernskioldMedia\LaravelPpt\SlideMasters\TwoColumn;
2329
use BernskioldMedia\LaravelPpt\SlideMasters\TwoUp;
2430
use Spatie\LaravelPackageTools\Package;
2531
use Spatie\LaravelPackageTools\PackageServiceProvider;
@@ -42,6 +48,7 @@ public function packageBooted(): void
4248
{
4349
// Register package's built-in slide masters
4450
SlideMasters::register([
51+
Agenda::class,
4552
Blank::class,
4653
BlankWithTitle::class,
4754
BlankWithTitleSubtitle::class,
@@ -52,10 +59,15 @@ public function packageBooted(): void
5259
ChartTitle::class,
5360
ChartTitles::class,
5461
FourUp::class,
62+
Quote::class,
5563
SixUp::class,
64+
Table::class,
5665
Text::class,
66+
ThreeColumn::class,
67+
ThreeUp::class,
5768
Title::class,
5869
TitleSubtitle::class,
70+
TwoColumn::class,
5971
TwoUp::class,
6072
]);
6173
}

src/SlideMasters/Agenda.php

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?php
2+
3+
namespace BernskioldMedia\LaravelPpt\SlideMasters;
4+
5+
use BernskioldMedia\LaravelPpt\Components\TextBox;
6+
use BernskioldMedia\LaravelPpt\Concerns\Slides\WithSlideTitle;
7+
use BernskioldMedia\LaravelPpt\Contracts\DynamicallyCreatable;
8+
use BernskioldMedia\LaravelPpt\Presentation\BaseSlide;
9+
use PhpOffice\PhpPresentation\Style\Bullet;
10+
use PhpOffice\PhpPresentation\Style\Color;
11+
12+
/**
13+
* @method static static make(string $title = '', array $items = [])
14+
*/
15+
class Agenda extends BaseSlide implements DynamicallyCreatable
16+
{
17+
use WithSlideTitle;
18+
19+
public function __construct(
20+
string $title = '',
21+
protected array $items = [],
22+
) {
23+
$this->slideTitle = $title;
24+
}
25+
26+
public function item(string $text): self
27+
{
28+
$this->items[] = $text;
29+
30+
return $this;
31+
}
32+
33+
protected function render(): void
34+
{
35+
if (! empty($this->slideTitle)) {
36+
$titleBox = $this->renderTitle();
37+
$yOffset = $titleBox->height + 75;
38+
} else {
39+
$yOffset = 75;
40+
}
41+
42+
if (empty($this->items)) {
43+
return;
44+
}
45+
46+
$box = null;
47+
48+
foreach ($this->items as $item) {
49+
if (! $box) {
50+
$box = TextBox::make($this, $item)
51+
->paragraphStyle('bodyText')
52+
->size(24)
53+
->height($this->presentation->height - $yOffset - 75)
54+
->width($this->presentation->width - (2 * $this->horizontalPadding))
55+
->position($this->horizontalPadding, $yOffset)
56+
->alignLeft()
57+
->alignTop()
58+
->render()
59+
->shape;
60+
} else {
61+
$box->createParagraph()
62+
->createTextRun($item)
63+
->getFont()
64+
->setSize(24)
65+
->setColor(new Color($this->textColor))
66+
->setBold(false)
67+
->setName($this->presentation->branding->baseFont())
68+
->setCharacterSpacing(0);
69+
}
70+
71+
$box->getActiveParagraph()
72+
->getBulletStyle()
73+
->setBulletType(Bullet::TYPE_NUMERIC)
74+
->setBulletColor(new Color($this->textColor));
75+
76+
$box->getActiveParagraph()
77+
->setSpacingAfter(25)
78+
->getAlignment()
79+
->setIndent(-40)
80+
->setMarginLeft(60);
81+
}
82+
}
83+
84+
public static function dataSchema(): array
85+
{
86+
return [
87+
'type' => 'object',
88+
'properties' => [
89+
'title' => [
90+
'type' => 'string',
91+
'description' => 'The slide title (e.g., "Agenda" or "Table of Contents")',
92+
],
93+
'items' => [
94+
'type' => 'array',
95+
'description' => 'Array of agenda items',
96+
'items' => [
97+
'type' => 'string',
98+
],
99+
],
100+
],
101+
'required' => ['title', 'items'],
102+
];
103+
}
104+
105+
public static function description(): string
106+
{
107+
return 'A slide with a title and numbered agenda/table of contents items';
108+
}
109+
110+
public static function exampleData(): array
111+
{
112+
return [
113+
'title' => 'Agenda',
114+
'items' => [
115+
'Introduction and Welcome',
116+
'Company Overview',
117+
'Product Demonstration',
118+
'Q&A Session',
119+
'Next Steps',
120+
],
121+
];
122+
}
123+
}

src/SlideMasters/Quote.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
namespace BernskioldMedia\LaravelPpt\SlideMasters;
4+
5+
use BernskioldMedia\LaravelPpt\Components\TextBox;
6+
use BernskioldMedia\LaravelPpt\Contracts\DynamicallyCreatable;
7+
use BernskioldMedia\LaravelPpt\Presentation\BaseSlide;
8+
9+
/**
10+
* @method static static make(string $quote = '', string $attribution = '')
11+
*/
12+
class Quote extends BaseSlide implements DynamicallyCreatable
13+
{
14+
public function __construct(
15+
protected string $quote = '',
16+
protected string $attribution = '',
17+
) {}
18+
19+
protected function render(): void
20+
{
21+
if (empty($this->quote)) {
22+
return;
23+
}
24+
25+
$quoteWidth = $this->presentation->width - (2 * $this->horizontalPadding) - 200;
26+
$quoteXOffset = $this->horizontalPadding + 100;
27+
28+
// Add opening quotation mark styling to the quote
29+
$formattedQuote = '"' . $this->quote . '"';
30+
31+
// Render the quote centered vertically
32+
$quoteBox = TextBox::make($this, $formattedQuote)
33+
->size(32)
34+
->bold()
35+
->width($quoteWidth)
36+
->position($quoteXOffset, ($this->presentation->height / 2) - 100)
37+
->alignCenter()
38+
->alignMiddle()
39+
->lines(6)
40+
->render();
41+
42+
// Render the attribution below the quote if provided
43+
if (! empty($this->attribution)) {
44+
TextBox::make($this, '' . $this->attribution)
45+
->size(20)
46+
->width($quoteWidth)
47+
->position($quoteXOffset, ($this->presentation->height / 2) + 50)
48+
->alignRight()
49+
->alignTop()
50+
->render();
51+
}
52+
}
53+
54+
public static function dataSchema(): array
55+
{
56+
return [
57+
'type' => 'object',
58+
'properties' => [
59+
'quote' => [
60+
'type' => 'string',
61+
'description' => 'The quote text to display',
62+
],
63+
'attribution' => [
64+
'type' => 'string',
65+
'description' => 'Attribution for the quote (author, source, etc.)',
66+
],
67+
],
68+
'required' => ['quote'],
69+
];
70+
}
71+
72+
public static function description(): string
73+
{
74+
return 'A slide with a large pull quote and optional attribution';
75+
}
76+
77+
public static function exampleData(): array
78+
{
79+
return [
80+
'quote' => 'The best way to predict the future is to invent it.',
81+
'attribution' => 'Alan Kay',
82+
];
83+
}
84+
}

src/SlideMasters/Table.php

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
namespace BernskioldMedia\LaravelPpt\SlideMasters;
4+
5+
use BernskioldMedia\LaravelPpt\Components\Table\Cell;
6+
use BernskioldMedia\LaravelPpt\Components\Table\Row;
7+
use BernskioldMedia\LaravelPpt\Components\Table\Table as TableComponent;
8+
use BernskioldMedia\LaravelPpt\Concerns\Slides\WithSlideTitle;
9+
use BernskioldMedia\LaravelPpt\Contracts\DynamicallyCreatable;
10+
use BernskioldMedia\LaravelPpt\Presentation\BaseSlide;
11+
use PhpOffice\PhpPresentation\Style\Alignment;
12+
use PhpOffice\PhpPresentation\Style\Color;
13+
14+
/**
15+
* @method static static make(string $title = '', array $headers = [], array $data = [])
16+
*/
17+
class Table extends BaseSlide implements DynamicallyCreatable
18+
{
19+
use WithSlideTitle;
20+
21+
public function __construct(
22+
string $title = '',
23+
protected array $headers = [],
24+
protected array $data = [],
25+
) {
26+
$this->slideTitle = $title;
27+
}
28+
29+
protected function render(): void
30+
{
31+
if (! empty($this->slideTitle)) {
32+
$titleBox = $this->renderTitle();
33+
$yOffset = $titleBox->height + 75;
34+
} else {
35+
$yOffset = 75;
36+
}
37+
38+
if (empty($this->headers) || empty($this->data)) {
39+
return;
40+
}
41+
42+
$columns = count($this->headers);
43+
$tableWidth = $this->presentation->width - (2 * $this->horizontalPadding);
44+
$columnWidth = (int) ($tableWidth / $columns);
45+
46+
$rows = [];
47+
48+
// Create header row
49+
$headerCells = [];
50+
foreach ($this->headers as $header) {
51+
$headerCells[] = Cell::make($header)
52+
->width($columnWidth)
53+
->bold()
54+
->backgroundColor(Color::COLOR_DARKBLUE)
55+
->color(Color::COLOR_WHITE)
56+
->alignCenter()
57+
->alignMiddle();
58+
}
59+
60+
$rows[] = Row::make($headerCells)->height(40);
61+
62+
// Create data rows
63+
foreach ($this->data as $rowData) {
64+
$dataCells = [];
65+
foreach ($rowData as $cellData) {
66+
$dataCells[] = Cell::make((string) $cellData)
67+
->width($columnWidth)
68+
->alignLeft()
69+
->alignMiddle();
70+
}
71+
$rows[] = Row::make($dataCells)->height(35);
72+
}
73+
74+
// Calculate table height
75+
$tableHeight = $this->presentation->height - $yOffset - 75;
76+
77+
TableComponent::make($this, $columns, $rows)
78+
->width($tableWidth)
79+
->height($tableHeight)
80+
->position($this->horizontalPadding, $yOffset)
81+
->render();
82+
}
83+
84+
public static function dataSchema(): array
85+
{
86+
return [
87+
'type' => 'object',
88+
'properties' => [
89+
'title' => [
90+
'type' => 'string',
91+
'description' => 'The slide title',
92+
],
93+
'headers' => [
94+
'type' => 'array',
95+
'description' => 'Array of table header labels',
96+
'items' => [
97+
'type' => 'string',
98+
],
99+
],
100+
'data' => [
101+
'type' => 'array',
102+
'description' => 'Array of table rows (each row is an array of cell values)',
103+
'items' => [
104+
'type' => 'array',
105+
'items' => [
106+
'type' => ['string', 'number'],
107+
],
108+
],
109+
],
110+
],
111+
'required' => ['title', 'headers', 'data'],
112+
];
113+
}
114+
115+
public static function description(): string
116+
{
117+
return 'A slide with a title and a formatted table with styling';
118+
}
119+
120+
public static function exampleData(): array
121+
{
122+
return [
123+
'title' => 'Quarterly Results',
124+
'headers' => ['Quarter', 'Revenue', 'Growth'],
125+
'data' => [
126+
['Q1 2024', '$2.5M', '15%'],
127+
['Q2 2024', '$3.2M', '28%'],
128+
['Q3 2024', '$3.8M', '19%'],
129+
['Q4 2024', '$4.1M', '8%'],
130+
],
131+
];
132+
}
133+
}

0 commit comments

Comments
 (0)