Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for custom renderers #164

Open
wants to merge 5 commits into
base: 1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/Contracts/Renderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Termwind\Contracts;

use Termwind\Components\Element;
use Termwind\ValueObjects\Node;

interface Renderer
{
/**
* Gets HTML content from a given node and converts to the content element.
*/
public function toElement(Node $node): Element;
}
14 changes: 14 additions & 0 deletions src/Exceptions/InvalidRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Termwind\Exceptions;

use InvalidArgumentException;

/**
* @internal
*/
final class InvalidRenderer extends InvalidArgumentException
{
}
3 changes: 2 additions & 1 deletion src/Html/CodeRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
namespace Termwind\Html;

use Termwind\Components\Element;
use Termwind\Contracts\Renderer;
use Termwind\Termwind;
use Termwind\ValueObjects\Node;

/**
* @internal
*/
final class CodeRenderer
final class CodeRenderer implements Renderer
{
public const TOKEN_DEFAULT = 'token_default';

Expand Down
82 changes: 82 additions & 0 deletions src/Html/ElementRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

namespace Termwind\Html;

use Termwind\Components\Element;
use Termwind\Contracts\Renderer;
use Termwind\Exceptions\InvalidRenderer;
use Termwind\ValueObjects\Node;

final class ElementRenderer
{
/**
* @var array<string, string>
WendellAdriel marked this conversation as resolved.
Show resolved Hide resolved
*/
private static array $renderers = [
'table' => TableRenderer::class,
'code' => CodeRenderer::class,
'pre' => PreRenderer::class,
];

/**
* @return array<string, string>
*/
public static function renderers(): array
{
return self::$renderers;
}

/**
* Checks if there is any renderer registered for the node name
*
* @param string $name
* @return bool
*/
public static function hasRenderer(string $name): bool
{
return array_key_exists($name, self::$renderers);
}

/**
* Registers a new renderer
*
* @param string $name
* @param string $renderer
* @return void
*
* @thows InvalidRenderer
*/
public static function register(string $name, string $renderer): void
{
$rendererObject = new $renderer();
if (! ($rendererObject instanceof Renderer)) {
throw new InvalidRenderer();
}
WendellAdriel marked this conversation as resolved.
Show resolved Hide resolved

self::$renderers[$name] = $renderer;
}

/**
* Renders the given Node
*
* @param Node $node
* @return Element
*
* @thows InvalidRenderer
*/
public static function render(Node $node): Element
{
$nodeName = $node->getName();

if (! self::hasRenderer($nodeName)) {
throw new InvalidRenderer();
}

/** @var Renderer $renderer */
$renderer = (new self::$renderers[$nodeName]);

return $renderer->toElement($node);
}
}
3 changes: 2 additions & 1 deletion src/Html/PreRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
namespace Termwind\Html;

use Termwind\Components\Element;
use Termwind\Contracts\Renderer;
use Termwind\Termwind;
use Termwind\ValueObjects\Node;

/**
* @internal
*/
final class PreRenderer
final class PreRenderer implements Renderer
{
/**
* Gets HTML content from a given node and converts to the content element.
Expand Down
3 changes: 2 additions & 1 deletion src/Html/TableRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Termwind\Components\Element;
use Termwind\Contracts\Renderer;
use Termwind\HtmlRenderer;
use Termwind\Termwind;
use Termwind\ValueObjects\Node;
Expand All @@ -20,7 +21,7 @@
/**
* @internal
*/
final class TableRenderer
final class TableRenderer implements Renderer
{
/**
* Symfony table object uses for table generation.
Expand Down
12 changes: 3 additions & 9 deletions src/HtmlRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@

use DOMDocument;
use DOMNode;
use Termwind\Html\CodeRenderer;
use Termwind\Html\PreRenderer;
use Termwind\Html\TableRenderer;
use Termwind\Html\ElementRenderer;
use Termwind\ValueObjects\Node;

/**
Expand Down Expand Up @@ -56,12 +54,8 @@ private function convert(Node $node): Components\Element|string
{
$children = [];

if ($node->isName('table')) {
return (new TableRenderer)->toElement($node);
} elseif ($node->isName('code')) {
return (new CodeRenderer)->toElement($node);
} elseif ($node->isName('pre')) {
return (new PreRenderer)->toElement($node);
if (ElementRenderer::hasRenderer($node->getName())) {
return ElementRenderer::render($node);
}

foreach ($node->getChildNodes() as $child) {
Expand Down
53 changes: 53 additions & 0 deletions tests/renderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

use Termwind\Components\Element;
use Termwind\Contracts\Renderer;
use Termwind\Html\ElementRenderer;
use Termwind\Termwind;
use Termwind\ValueObjects\Node;

it('checks that default renderers are registered', function ($name) {
expect(ElementRenderer::hasRenderer($name))
->toBeTrue();
})->with(['code', 'pre', 'table']);

it('adds custom renderer', function () {
expect(ElementRenderer::hasRenderer('custom'))
->toBeFalse();

ElementRenderer::register('custom', CustomRenderer::class);

expect(ElementRenderer::hasRenderer('custom'))
->toBeTrue();
});

final class CustomRenderer implements Renderer
{
public function toElement(Node $node): Element
{
$lines = explode("\n", $node->getHtml());
if (reset($lines) === '') {
array_shift($lines);
}

if (end($lines) === '') {
array_pop($lines);
}

$maxStrLen = array_reduce(
$lines,
static fn (int $max, string $line) => ($max < strlen($line)) ? strlen($line) : $max,
0
);

$styles = $node->getClassAttribute();
$html = array_map(
static fn (string $line) => (string) Termwind::div(str_pad($line, $maxStrLen + 3), $styles),
$lines
);

return Termwind::raw(
implode('', $html)
);
}
}