Skip to content
Open
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
10 changes: 6 additions & 4 deletions Documentation-rendertest/ImagesAndFigures/FloatAndAlignment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Image float left

.. |example-teaser-left| image:: ../images/q150_cccccc.png
:alt: Left floating image
:class: float-left with-shadow
:class: float-start with-shadow

|example-teaser-left|
Typesetting is the composition of text by means of arranging physical
Expand All @@ -41,7 +41,7 @@ Image float right

.. |example-teaser-right| image:: ../images/q150_cccccc.png
:alt: Right floating image
:class: float-right with-shadow
:class: float-end with-shadow

|example-teaser-right|
Typesetting is the composition of text by means of arranging physical
Expand All @@ -62,7 +62,8 @@ Figure float left

.. figure:: ../images/q150_cccccc.png
:alt: Left floating figure
:class: float-left with-shadow
:align: left
:class: with-shadow

A figure floated to the left

Expand All @@ -85,7 +86,8 @@ Figure float right

.. figure:: ../images/q150_cccccc.png
:alt: Right floating figure
:class: float-right with-shadow
:align: right
:class: with-shadow

A figure floated to the right

Expand Down
7 changes: 7 additions & 0 deletions packages/typo3-docs-theme/assets/sass/components/_images.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@ figure {
&:not(:first-child) {
@extend .mt-3;
}
&.float-start,
&.float-end,
// Deprecated: legacy Bootstrap 4 class names — remove after migration period (see #1179)
&.float-left,
&.float-right {
margin-bottom: $spacer;
max-width: 50%;
}
&.float-start,
&.float-left {
float: left;
margin-right: $spacer;
}
&.float-end,
&.float-right {
float: right;
margin-left: $spacer;
Expand All @@ -37,6 +42,8 @@ figure {
}

@media (max-width: 575.98px) {
figure.float-start,
figure.float-end,
figure.float-left,
figure.float-right {
float: none;
Expand Down
14 changes: 14 additions & 0 deletions packages/typo3-docs-theme/assets/sass/components/_rst.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@
display: inline-block;
max-width: 100%;
height: auto;
// Deprecated: legacy Bootstrap 4 class names — remove after migration period (see #1179)
&.float-start,
&.float-left {
float: left;
margin-right: $spacer;
margin-bottom: $spacer;
}
&.float-end,
&.float-right {
float: right;
margin-left: $spacer;
Expand All @@ -57,6 +60,17 @@
}
}
}
@media (max-width: 575.98px) {
img.float-start,
img.float-end,
img.float-left,
img.float-right {
float: none;
max-width: 100%;
margin-left: 0;
margin-right: 0;
}
}
.plantuml object {
height: auto !important;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use T3Docs\Typo3DocsTheme\Directives\ConfvalMenuDirective;
use T3Docs\Typo3DocsTheme\Directives\DirectoryTreeDirective;
use T3Docs\Typo3DocsTheme\Directives\FigureDirective;
use T3Docs\Typo3DocsTheme\Directives\ImageDirective;
use T3Docs\Typo3DocsTheme\Directives\GlossaryDirective;
use T3Docs\Typo3DocsTheme\Directives\GroupTabDirective;
use T3Docs\Typo3DocsTheme\Directives\IncludeDirective;
Expand Down Expand Up @@ -203,6 +204,7 @@
->set(DirectoryTreeDirective::class)
->set(FigureDirective::class)
->arg('$startingRule', service(DirectiveContentRule::class))
->set(ImageDirective::class)
->set(GlossaryDirective::class)
->set(GroupTabDirective::class)
->set(IncludeDirective::class)
Expand Down
23 changes: 14 additions & 9 deletions packages/typo3-docs-theme/resources/public/css/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -24700,15 +24700,15 @@ dl.command .command-arguments section {
figure figcaption p:last-child {
margin-bottom: 0;
}
figure.float-left, figure.float-right {
figure.float-start, figure.float-end, figure.float-left, figure.float-right {
margin-bottom: 1rem;
max-width: 50%;
}
figure.float-left {
figure.float-start, figure.float-left {
float: left;
margin-right: 1rem;
}
figure.float-right {
figure.float-end, figure.float-right {
float: right;
margin-left: 1rem;
}
Expand All @@ -24718,6 +24718,8 @@ figure.align-center {
}

@media (max-width: 575.98px) {
figure.float-start,
figure.float-end,
figure.float-left,
figure.float-right {
float: none;
Expand Down Expand Up @@ -24821,13 +24823,15 @@ article *:hover > a.headerlink, article *:hover > a.permalink, article *:hover .
max-width: 100%;
height: auto;
}
.rst-content img.float-left,
.rst-content img.float-start, .rst-content img.float-left,
.rst-content object.float-start,
.rst-content object.float-left {
float: left;
margin-right: 1rem;
margin-bottom: 1rem;
}
.rst-content img.float-right,
.rst-content img.float-end, .rst-content img.float-right,
.rst-content object.float-end,
.rst-content object.float-right {
float: right;
margin-left: 1rem;
Expand All @@ -24840,13 +24844,14 @@ article *:hover > a.headerlink, article *:hover > a.permalink, article *:hover .
margin-right: auto;
}
@media (max-width: 575.98px) {
.rst-content img.float-left, .rst-content img.float-right,
.rst-content object.float-left,
.rst-content object.float-right {
.rst-content img.float-start,
.rst-content img.float-end,
.rst-content img.float-left,
.rst-content img.float-right {
float: none;
max-width: 100%;
margin-left: 0;
margin-right: 0;
max-width: 100%;
}
}
.rst-content .plantuml object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
{%- set alignMap = {'left': 'float-left', 'right': 'float-right', 'center': 'align-center'} -%}
{# Maps RST :align: values to CSS classes. Duplicated in image.html.twig — keep in sync. #}
{# See also: FigureDirective.php / ImageDirective.php for :class: float-left/right rewriting #}
{%- set alignMap = {'left': 'float-start', 'right': 'float-end', 'center': 'align-center'} -%}
{%- set alignClass = node.hasOption('align') ? alignMap[node.option('align')]|default('') : '' -%}
{%- set classes = ([node.classesString, alignClass]|join(' '))|trim -%}
{%- set classes = node.classesString|trim -%}
{%- if alignClass and alignClass not in (classes ? classes|split(' ') : []) -%}
{%- set classes = ([classes, alignClass]|join(' '))|trim -%}
{%- endif -%}
<figure
{%- if classes %} class="{{ classes }}"{% endif -%}
{%- if node.hasOption('figwidth') %} style="{% if node.hasOption('figwidth') %}width: {{ node.option('figwidth') }};{% endif %}"{% endif -%}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
{%- if node.hasOption('zoom') -%}
<span data-zoom="{{ node.option('zoom') }}"{% if node.hasOption('gallery') %} data-gallery="{{ node.option('gallery') }}"{% endif %}{% if node.hasOption('zoom-factor') %} data-zoom-factor="{{ node.option('zoom-factor') }}"{% endif %}{% if node.hasOption('zoom-indicator') %} data-zoom-indicator="{{ node.option('zoom-indicator') }}"{% endif %}>
{%- endif -%}
{%- set alignMap = {'left': 'float-left', 'right': 'float-right', 'center': 'align-center'} -%}
{# Maps RST :align: values to CSS classes. Duplicated in figure.html.twig — keep in sync. #}
{# See also: FigureDirective.php / ImageDirective.php for :class: float-left/right rewriting #}
{%- set alignMap = {'left': 'float-start', 'right': 'float-end', 'center': 'align-center'} -%}
{%- set alignClass = node.hasOption('align') ? alignMap[node.option('align')]|default('') : '' -%}
{%- set classes = ([node.classesString, alignClass]|join(' '))|trim -%}
{%- set classes = node.classesString|trim -%}
{%- if alignClass and alignClass not in (classes ? classes|split(' ') : []) -%}
{%- set classes = ([classes, alignClass]|join(' '))|trim -%}
{%- endif -%}
<img
src="{%- if node.value is external_target -%} {{ node.value }} {%- else -%} {{ asset(node.value) }} {%- endif -%}"
{% if node.hasOption('width') %}width="{{ node.option('width') }}"{% endif%}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use phpDocumentor\Guides\NodeRenderers\TemplateNodeRenderer;
use phpDocumentor\Guides\RestructuredText\Directives\FigureDirective as BaseFigureDirective;
use phpDocumentor\Guides\RestructuredText\Directives\ImageDirective as BaseImageDirective;
use phpDocumentor\Guides\TemplateRenderer;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
Expand All @@ -15,7 +16,6 @@
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use T3Docs\Typo3DocsTheme\Directives\FigureDirective;
use T3Docs\Typo3DocsTheme\Nodes\Inline\CodeInlineNode;
use T3Docs\Typo3DocsTheme\Nodes\Inline\ComposerInlineNode;
use T3Docs\Typo3DocsTheme\Nodes\Inline\FileInlineNode;
Expand Down Expand Up @@ -114,14 +114,19 @@ public function prepend(ContainerBuilder $container): void
}

/**
* Remove the base library's FigureDirective in favor of our custom implementation
* that supports zoom functionality.
* Remove the base library's directives in favor of our custom implementations.
*
* - FigureDirective: supports zoom functionality and float class deprecation
* - ImageDirective: uses composition to add float class deprecation handling
*/
public function process(ContainerBuilder $container): void
{
// Remove the base library's FigureDirective to let our custom one take over
if ($container->hasDefinition(BaseFigureDirective::class)) {
$container->removeDefinition(BaseFigureDirective::class);
}

if ($container->hasDefinition(BaseImageDirective::class)) {
$container->removeDefinition(BaseImageDirective::class);
}
}
}
52 changes: 41 additions & 11 deletions packages/typo3-docs-theme/src/Directives/FigureDirective.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@
use phpDocumentor\Guides\RestructuredText\Directives\BaseDirective;
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
use phpDocumentor\Guides\RestructuredText\Parser\DirectiveOption;
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
use Psr\Log\LoggerInterface;

use function array_filter;
use function dirname;
use function explode;
use function implode;
use function in_array;
use function is_string;
use function preg_replace;
use function trim;

/**
* Renders a figure with optional zoom functionality.
Expand Down Expand Up @@ -55,12 +58,15 @@
*/
final class FigureDirective extends BaseDirective
{
use RewritesLegacyFloatClasses;

private const VALID_ZOOM_MODES = ['lightbox', 'gallery', 'inline', 'lens'];

/** @param Rule<CollectionNode> $startingRule */
public function __construct(
private readonly DocumentNameResolverInterface $documentNameResolver,
private readonly Rule $startingRule,
private readonly LoggerInterface $logger,
) {}

public function getName(): string
Expand All @@ -85,16 +91,39 @@ public function process(
dirname($blockContext->getDocumentParserContext()->getContext()->getCurrentAbsolutePath()),
$directive->getData(),
));
// Strip float classes from the inner image - floating should only
// apply to the <figure> element to keep the caption below the image.
// Note: Currently the framework's postProcessNode() only processes the
// top-level FigureNode, so the inner image's classesString is empty.
// We strip here for forward-compatibility should this behavior change.
$imageClass = isset($scalarOptions['class']) && is_string($scalarOptions['class'])
// Handle float classes on the figure element
$figureClass = isset($scalarOptions['class']) && is_string($scalarOptions['class'])
? $scalarOptions['class']
: null;
if ($imageClass !== null) {
$imageClass = trim((string) preg_replace('/\bfloat-(left|right)\b/', '', $imageClass)) ?: null;

if ($figureClass !== null) {
// Detect and rewrite legacy float-left/float-right to float-start/float-end
// See also: figure.html.twig / image.html.twig alignMap for :align: option mapping
if ($this->hasLegacyFloatClass($figureClass)) {
$this->logger->warning(
'Using `:class: float-left` / `:class: float-right` is deprecated. '
. 'Use `:align: left` / `:align: right` instead.',
$blockContext->getLoggerInformation(),
);
$figureClass = $this->rewriteLegacyFloatClasses($figureClass);
// Update the raw directive option so that DirectiveRule::postProcessNode
// uses the rewritten class when calling setClasses() on the node
$directive->addOption(new DirectiveOption('class', $figureClass));
}
}

// Strip float classes from the inner image — floating should only apply
// to the <figure> element to keep the caption below the image.
// Non-float classes (e.g. with-shadow) are still propagated to the
// inner <img> so they can style the image itself.
if ($figureClass !== null) {
$imageClasses = array_filter(
explode(' ', $figureClass),
static fn(string $class): bool => !in_array($class, ['float-start', 'float-end'], true),
);
$imageClass = $imageClasses !== [] ? implode(' ', $imageClasses) : null;
} else {
$imageClass = null;
}

$image = $image->withOptions([
Expand All @@ -121,7 +150,8 @@ public function process(
$filteredOptions['zoom'] = null;
}

// Remove options that are already handled by the image node
// Remove options that are already handled by the image node or by
// DirectiveRule::postProcessNode (class is handled via setClasses())
unset(
$filteredOptions['width'],
$filteredOptions['height'],
Expand Down
Loading
Loading