Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
120 changes: 120 additions & 0 deletions php-transformer/src/HtmlToBlocks/Elements/InlineFlowLowering.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

declare(strict_types=1);

namespace Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Elements;

use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Classification\SourceElementClassifier;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Patterns\PatternTreeConverter;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\RichText\RichTextMaterializer;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\SourceBlockCreator;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Style\StyleResolver;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Support\SourceDom;
use Automattic\BlocksEngine\PhpTransformer\Support\ShellLandmarkPolicy;
use Automattic\BlocksEngine\PhpTransformer\WordPress\Runtime;
use DOMElement;

/**
* Lowers inline flow wrappers: a div of inline content split by line breaks
* becomes one paragraph, and an inline token group becomes a group of
* paragraphs.
*/
final class InlineFlowLowering
{
public function __construct(
private readonly SourceBlockCreator $blocks,
private readonly PatternTreeConverter $tree,
private readonly StyleResolver $styleResolver,
private readonly SourceElementClassifier $sourceElementClassifier,
private readonly RichTextMaterializer $richTextMaterializer,
private readonly Runtime $runtime
) {
}

/**
* @return array<string, mixed>|null
*/
public function textFlowBlockFromElement(DOMElement $element): ?array
{
if ( 'div' !== strtolower($element->tagName) || '' !== trim(SourceDom::attr($element, 'id')) || '' !== trim(SourceDom::attr($element, 'role')) ) {
return null;
}

$hasLineBreak = false;
foreach ( $element->childNodes as $child ) {
if ( ! $child instanceof DOMElement ) {
continue;
}

$tagName = strtolower($child->tagName);
if ( 'br' === $tagName ) {
$hasLineBreak = true;
}
if ( 'br' !== $tagName && ! $this->sourceElementClassifier->isInlineContentElement($tagName) && 'a' !== $tagName ) {
return null;
}
}

if ( ! $hasLineBreak ) {
return null;
}

$content = $this->richTextMaterializer->contentWithoutDecorativeSvg($element);
if ( '' === trim($this->runtime->stripAllTags($content)) ) {
return null;
}

return $this->blocks->createBlock('core/paragraph', array_merge($this->styleResolver->presentationAttributes($element), array( 'content' => $content )), array(), $element);
}

public function inlineTokenGroupBlockFromElement(DOMElement $element, array &$fallbacks): ?array
{
if ( ! ShellLandmarkPolicy::isInlineTokenContainerTag($element->tagName) ) {
return null;
}

if ( ! $this->sourceElementClassifier->hasInlineTokenGroupSignal($element) ) {
return null;
}

$children = array();
foreach ( $element->childNodes as $child ) {
if ( XML_TEXT_NODE === $child->nodeType ) {
if ( '' !== trim($child->textContent ?? '') ) {
return null;
}
continue;
}

if ( ! $child instanceof DOMElement ) {
continue;
}

if ( ! $this->sourceElementClassifier->isInlineTokenItemElement($child) ) {
return null;
}

$tagName = strtolower($child->tagName);
if ( in_array($tagName, array( 'a', 'button' ), true) ) {
$block = $this->tree->element($child, $fallbacks, true);
if ( null === $block ) {
return null;
}
$children[] = $block;
continue;
}

$content = $this->styleResolver->innerHtml($child);
if ( '' === trim($this->runtime->stripAllTags($content)) ) {
return null;
}
$children[] = $this->blocks->createBlock('core/paragraph', array_merge($this->styleResolver->presentationAttributes($child), array( 'content' => $content )), array(), $child);
}

if ( count($children) < 2 ) {
return null;
}

return $this->blocks->createBlock('core/group', $this->styleResolver->presentationAttributes($element), $children, $element);
}
}
93 changes: 4 additions & 89 deletions php-transformer/src/HtmlToBlocks/HtmlCompilation.php
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ final class HtmlCompilation extends SourceBlockCreator implements RichTextInline
private readonly SvgMaterializer $svgMaterializer;
private readonly Elements\AuthoredStateButtonLowering $authoredStateButtons;
private readonly Elements\InlineSvgTextGroupLowering $inlineSvgTextGroups;
private readonly Elements\InlineFlowLowering $inlineFlows;

private readonly RichTextMaterializer $richTextMaterializer;
private readonly Style\SourceElementVisibility $sourceVisibility;
Expand Down Expand Up @@ -525,6 +526,7 @@ public function __construct(
$this->flankedSeparators = new Elements\FlankedSeparatorLowering($this, $this->styleResolver, $this->sourceElementClassifier);
$this->sourceVisibility = new Style\SourceElementVisibility($this->styleResolver);
$this->richTextMaterializer = new RichTextMaterializer($this->styleResolver, $this->svgMaterializer, $this->session, $this);
$this->inlineFlows = new Elements\InlineFlowLowering($this, $this, $this->styleResolver, $this->sourceElementClassifier, $this->richTextMaterializer, $this->runtime);
$this->authoredStateButtons = new Elements\AuthoredStateButtonLowering($this->session, $this->svgMaterializer);
$this->linkedIconTextRows = new Elements\LinkedIconTextRowLowering(
$this,
Expand Down Expand Up @@ -1045,14 +1047,14 @@ function (DOMElement $element, array &$fallbacks, bool $captureUnsupported): arr
},
mediaGalleryBlock: fn (DOMElement $element, array &$fallbacks): ?array => $this->mediaGalleryBlockFromElement($element, $fallbacks),
namePriceRowBlock: fn (DOMElement $element, array &$fallbacks): ?array => $this->namePriceRowBlockFromElement($element, $fallbacks),
inlineTokenGroupBlock: fn (DOMElement $element, array &$fallbacks): ?array => $this->inlineTokenGroupBlockFromElement($element, $fallbacks),
inlineTokenGroupBlock: fn (DOMElement $element, array &$fallbacks): ?array => $this->inlineFlows->inlineTokenGroupBlockFromElement($element, $fallbacks),
visualTextWrapperBlock: fn (DOMElement $element): ?array => $this->visualTextWrapperBlockFromElement($element),
standaloneSearchBlock: fn (DOMElement $element): ?array => $this->searchBlockConverter->searchBlockFromStandaloneControl($element),
readableFormControlBlock: fn (DOMElement $element): ?array => $this->readableFormControlBlockConverter->convert($element),
cssAuthoredMarqueeBlock: fn (DOMElement $element): ?array => $this->authoredMarqueeRecognizer->cssAuthoredMarqueeBlock($element),
authoredCarouselBlock: fn (DOMElement $element): ?array => $this->authoredCarouselGenerator->convert($element),
generatedComponentBlock: fn (DOMElement $element, array &$fallbacks): ?array => $this->nativeOrGeneratedComponentBlock($element, $fallbacks),
textFlowBlock: fn (DOMElement $element): ?array => $this->textFlowBlockFromElement($element),
textFlowBlock: fn (DOMElement $element): ?array => $this->inlineFlows->textFlowBlockFromElement($element),
convertChildren: fn (DOMElement $element, array &$fallbacks): array => $this->convertChildren($element, $fallbacks, true),
backgroundImageBlock: fn (DOMElement $element): ?array => $this->imageLowering->backgroundImageBlock($element),
coalescedSingleGroupWrapper: fn (DOMElement $element, array $child): ?array => $this->coalescedSingleGroupWrapper($element, $child),
Expand Down Expand Up @@ -5096,42 +5098,6 @@ private function authoredDisplay(DOMElement $element): string
return $this->authorLayoutOwnership->authoredDisplay($element);
}

/**
* @return array<string, mixed>|null
*/
private function textFlowBlockFromElement(DOMElement $element): ?array
{
if ( 'div' !== strtolower($element->tagName) || '' !== trim($this->attr($element, 'id')) || '' !== trim($this->attr($element, 'role')) ) {
return null;
}

$hasLineBreak = false;
foreach ( $element->childNodes as $child ) {
if ( ! $child instanceof DOMElement ) {
continue;
}

$tagName = strtolower($child->tagName);
if ( 'br' === $tagName ) {
$hasLineBreak = true;
}
if ( 'br' !== $tagName && ! $this->sourceElementClassifier->isInlineContentElement($tagName) && 'a' !== $tagName ) {
return null;
}
}

if ( ! $hasLineBreak ) {
return null;
}

$content = $this->richTextMaterializer->contentWithoutDecorativeSvg($element);
if ( '' === trim($this->runtime->stripAllTags($content)) ) {
return null;
}

return $this->createBlock('core/paragraph', array_merge($this->styleResolver->presentationAttributes($element), array( 'content' => $content )), array(), $element);
}

/**
* Lower a foreignObject-only "fit text" SVG to the native text block its
* inner element renders as (`<svg viewBox="..."><foreignObject width="100%"
Expand Down Expand Up @@ -5174,57 +5140,6 @@ private function foreignObjectFitTextBlock(DOMElement $svg, array &$fallbacks):
return $this->createBlock('core/group', $this->styleResolver->presentationAttributes($svg), array($this->blockFactory->rebuild($block, $attrs)), $svg);
}

private function inlineTokenGroupBlockFromElement(DOMElement $element, array &$fallbacks): ?array
{
if ( ! ShellLandmarkPolicy::isInlineTokenContainerTag($element->tagName) ) {
return null;
}

if ( ! $this->sourceElementClassifier->hasInlineTokenGroupSignal($element) ) {
return null;
}

$children = array();
foreach ( $element->childNodes as $child ) {
if ( XML_TEXT_NODE === $child->nodeType ) {
if ( '' !== trim($child->textContent ?? '') ) {
return null;
}
continue;
}

if ( ! $child instanceof DOMElement ) {
continue;
}

if ( ! $this->sourceElementClassifier->isInlineTokenItemElement($child) ) {
return null;
}

$tagName = strtolower($child->tagName);
if ( in_array($tagName, array( 'a', 'button' ), true) ) {
$block = $this->convertElement($child, $fallbacks, true);
if ( null === $block ) {
return null;
}
$children[] = $block;
continue;
}

$content = $this->styleResolver->innerHtml($child);
if ( '' === trim($this->runtime->stripAllTags($content)) ) {
return null;
}
$children[] = $this->createBlock('core/paragraph', array_merge($this->styleResolver->presentationAttributes($child), array( 'content' => $content )), array(), $child);
}

if ( count($children) < 2 ) {
return null;
}

return $this->createBlock('core/group', $this->styleResolver->presentationAttributes($element), $children, $element);
}

private function visualTextWrapperBlockFromElement(DOMElement $element): ?array
{
if ( ! in_array(strtolower($element->tagName), array( 'div', 'span' ), true) || $this->sourceElementClassifier->hasBlockContentChildren($element) ) {
Expand Down
Loading