%PDF- %PDF-
Direktori : /home/silvzytp/ccd-ind-code/vendor/nunomaduro/termwind/src/Html/ |
Current File : //home/silvzytp/ccd-ind-code/vendor/nunomaduro/termwind/src/Html/TableRenderer.php |
<?php declare(strict_types=1); namespace Termwind\Html; use Iterator; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Helper\TableCell; use Symfony\Component\Console\Helper\TableCellStyle; use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\OutputInterface; use Termwind\Components\Element; use Termwind\HtmlRenderer; use Termwind\Termwind; use Termwind\ValueObjects\Node; use Termwind\ValueObjects\Styles; /** * @internal */ final class TableRenderer { /** * Symfony table object uses for table generation. */ private Table $table; /** * This object is used for accumulating output data from Symfony table object and return it as a string. */ private BufferedOutput $output; public function __construct() { $this->output = new BufferedOutput( // Content should output as is, without changes OutputInterface::VERBOSITY_NORMAL | OutputInterface::OUTPUT_RAW, true ); $this->table = new Table($this->output); } /** * Converts table output to the content element. */ public function toElement(Node $node): Element { $this->parseTable($node); $this->table->render(); $content = preg_replace('/\n$/', '', $this->output->fetch()) ?? ''; return Termwind::div($content, '', [ 'isFirstChild' => $node->isFirstChild(), ]); } /** * Looks for thead, tfoot, tbody, tr elements in a given DOM and appends rows from them to the Symfony table object. */ private function parseTable(Node $node): void { $style = $node->getAttribute('style'); if ($style !== '') { $this->table->setStyle($style); } foreach ($node->getChildNodes() as $child) { match ($child->getName()) { 'thead' => $this->parseHeader($child), 'tfoot' => $this->parseFoot($child), 'tbody' => $this->parseBody($child), default => $this->parseRows($child) }; } } /** * Looks for table header title and tr elements in a given thead DOM node and adds them to the Symfony table object. */ private function parseHeader(Node $node): void { $title = $node->getAttribute('title'); if ($title !== '') { $this->table->getStyle()->setHeaderTitleFormat( $this->parseTitleStyle($node) ); $this->table->setHeaderTitle($title); } foreach ($node->getChildNodes() as $child) { if ($child->isName('tr')) { foreach ($this->parseRow($child) as $row) { if (! is_array($row)) { continue; } $this->table->setHeaders($row); } } } } /** * Looks for table footer and tr elements in a given tfoot DOM node and adds them to the Symfony table object. */ private function parseFoot(Node $node): void { $title = $node->getAttribute('title'); if ($title !== '') { $this->table->getStyle()->setFooterTitleFormat( $this->parseTitleStyle($node) ); $this->table->setFooterTitle($title); } foreach ($node->getChildNodes() as $child) { if ($child->isName('tr')) { $rows = iterator_to_array($this->parseRow($child)); if (count($rows) > 0) { $this->table->addRow(new TableSeparator()); $this->table->addRows($rows); } } } } /** * Looks for tr elements in a given DOM node and adds them to the Symfony table object. */ private function parseBody(Node $node): void { foreach ($node->getChildNodes() as $child) { if ($child->isName('tr')) { $this->parseRows($child); } } } /** * Parses table tr elements. */ private function parseRows(Node $node): void { foreach ($this->parseRow($node) as $row) { $this->table->addRow($row); } } /** * Looks for th, td elements in a given DOM node and converts them to a table cells. * * @return Iterator<array<int, TableCell>|TableSeparator> */ private function parseRow(Node $node): Iterator { $row = []; foreach ($node->getChildNodes() as $child) { if ($child->isName('th') || $child->isName('td')) { $align = $child->getAttribute('align'); $class = $child->getClassAttribute(); if ($child->isName('th')) { $class .= ' strong'; } $text = (string) (new HtmlRenderer)->parse( trim(preg_replace('/<br\s?+\/?>/', "\n", $child->getHtml()) ?? '') ); if ((bool) preg_match(Styles::STYLING_REGEX, $text)) { $class .= ' font-normal'; } $row[] = new TableCell( // I need only spaces after applying margin, padding and width except tags. // There is no place for tags, they broke cell formatting. (string) Termwind::span($text, $class), [ // Gets rowspan and colspan from tr and td tag attributes 'colspan' => max((int) $child->getAttribute('colspan'), 1), 'rowspan' => max((int) $child->getAttribute('rowspan'), 1), // There are background and foreground and options 'style' => $this->parseCellStyle( $class, $align === '' ? TableCellStyle::DEFAULT_ALIGN : $align ), ] ); } } if ($row !== []) { yield $row; } $border = (int) $node->getAttribute('border'); for ($i = $border; $i--; $i > 0) { yield new TableSeparator(); } } /** * Parses tr, td tag class attribute and passes bg, fg and options to a table cell style. */ private function parseCellStyle(string $styles, string $align = TableCellStyle::DEFAULT_ALIGN): TableCellStyle { // I use this empty span for getting styles for bg, fg and options // It will be a good idea to get properties without element object and then pass them to an element object $element = Termwind::span('%s', $styles); $styles = []; $colors = $element->getProperties()['colors'] ?? []; foreach ($colors as $option => $content) { if (in_array($option, ['fg', 'bg'], true)) { $content = is_array($content) ? array_pop($content) : $content; $styles[] = "$option=$content"; } } // If there are no styles we don't need extra tags if ($styles === []) { $cellFormat = '%s'; } else { $cellFormat = '<'.implode(';', $styles).'>%s</>'; } return new TableCellStyle([ 'align' => $align, 'cellFormat' => $cellFormat, ]); } /** * Get styled representation of title. */ private function parseTitleStyle(Node $node): string { return (string) Termwind::span(' %s ', $node->getClassAttribute()); } }