Skip to content

Commit 41af386

Browse files
committed
[FEATURE] Add f:format.cssVariables ViewHelper
1 parent 2aa9f1d commit 41af386

4 files changed

Lines changed: 251 additions & 1 deletion

File tree

Documentation/ViewHelpers/Fluid.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.. This reStructured text file has been automatically generated, do not change.
2+
.. Source: https://github.com/TYPO3/Fluid/blob/main/src/ViewHelpers/Format/CssVariablesViewHelper.php
3+
4+
:edit-on-github-link: https://github.com/TYPO3/Fluid/edit/main/src/ViewHelpers/Format/CssVariablesViewHelper.php
5+
:navigation-title: format.cssVariables
6+
7+
.. include:: /Includes.rst.txt
8+
9+
.. _typo3fluid-fluid-format-cssvariables:
10+
11+
========================================================
12+
Format.cssVariables ViewHelper `<f:format.cssVariables>`
13+
========================================================
14+
15+
.. .. note::
16+
.. This reference is part of the documentation of Fluid Standalone.
17+
.. If you are working with Fluid in TYPO3 CMS, please refer to
18+
.. :doc:`TYPO3's ViewHelper reference <t3viewhelper:Global/Format/CssVariables>` instead.
19+
20+
.. typo3:viewhelper:: format.cssVariables
21+
:source: ../../Fluid.json
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file belongs to the package "TYPO3 Fluid".
7+
* See LICENSE.txt that was shipped with this package.
8+
*/
9+
10+
namespace TYPO3Fluid\Fluid\ViewHelpers\Format;
11+
12+
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
13+
14+
/**
15+
* ViewHelper to provide fluid variables as CSS variables.
16+
*
17+
* The css variable names are created from the array keys.
18+
* You can add a custom prefix to the CSS variable name.
19+
*
20+
* Optionally, a CSS selector (e.g. :root) can be supplied
21+
* which will be wrapped around the CSS variables.
22+
*
23+
* Examples
24+
* ========
25+
*
26+
* Converting a view variable
27+
* ------------------------
28+
*
29+
* ::
30+
*
31+
* {someArray -> f:format.cssVariables()}
32+
*
33+
* ``--key: value; --key2: value2;``
34+
* Depending on the value of ``{someArray}``.
35+
*
36+
* Associative array without selector for inline usage
37+
* -----------------
38+
*
39+
* ::
40+
*
41+
* {f:format.cssVariables(value: {foo: 'bar', bar: 'baz'})}
42+
*
43+
* ``--foo: bar; --bar: baz;``
44+
*
45+
* Nested array with prefix and custom selector
46+
* ----------------------------------------
47+
*
48+
* ::
49+
*
50+
* {f:format.cssVariables(value: {foo: 'bar', bar: {baz: 'qux'}}, prefix: 'color', selector: '.my-class')}
51+
*
52+
* ``.my-class {
53+
* --color-foo: bar;
54+
* --color-bar-baz: qux;
55+
* }``
56+
*/
57+
final class CssVariablesViewHelper extends AbstractViewHelper
58+
{
59+
/**
60+
* @var bool
61+
*/
62+
protected $escapeChildren = false;
63+
64+
public function initializeArguments(): void
65+
{
66+
$this->registerArgument('value', 'array', 'Input array which should be provided as CSS variables');
67+
$this->registerArgument('prefix', 'string', 'Prefix used in the CSS variables name', false, '');
68+
$this->registerArgument('selector', 'string', 'Define CSS selector/s for the CSS variables', false, '');
69+
}
70+
71+
public function render(): string
72+
{
73+
$value = $this->arguments['value'];
74+
75+
if ($value === null) {
76+
$value = $this->renderChildren();
77+
}
78+
79+
if (!is_iterable($value)) {
80+
return '';
81+
}
82+
83+
$prefix = is_string($this->arguments['prefix']) ? $this->arguments['prefix'] : '';
84+
if (!empty($prefix)) {
85+
$prefix .= '-';
86+
}
87+
88+
if (!empty($this->arguments['selector'])) {
89+
return $this->arguments['selector'] . ' {' . PHP_EOL . $this->buildVariables($value, $prefix, PHP_EOL) . '}';
90+
}
91+
92+
return trim($this->buildVariables($value, $prefix));
93+
}
94+
95+
/**
96+
* Explicitly set argument name to be used as content.
97+
*/
98+
public function getContentArgumentName(): string
99+
{
100+
return 'value';
101+
}
102+
103+
private function buildVariables(iterable $variables, string $prefix = '', string $separator = ' '): string
104+
{
105+
$content = '';
106+
107+
foreach ($variables as $key => $value) {
108+
if (is_iterable($value)) {
109+
$content .= self::buildVariables($value, $prefix . $key . '-', $separator);
110+
} elseif (is_scalar($value) || $value instanceof \Stringable) {
111+
$content .= '--' . strtolower($prefix . $key) . ': ' . $value . ';' . $separator;
112+
}
113+
}
114+
return $content;
115+
}
116+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file belongs to the package "TYPO3 Fluid".
7+
* See LICENSE.txt that was shipped with this package.
8+
*/
9+
10+
namespace TYPO3Fluid\Fluid\Tests\Functional\ViewHelpers\Format;
11+
12+
use PHPUnit\Framework\Attributes\DataProvider;
13+
use PHPUnit\Framework\Attributes\Test;
14+
use TYPO3Fluid\Fluid\Tests\Functional\AbstractFunctionalTestCase;
15+
use TYPO3Fluid\Fluid\Tests\Functional\Fixtures\Various\IterableExample;
16+
use TYPO3Fluid\Fluid\View\TemplateView;
17+
18+
final class CssVariablesViewHelperTest extends AbstractFunctionalTestCase
19+
{
20+
private static array $value = [
21+
'red' => '#ff0000',
22+
'green' => 'rgb(0, 255, 0)',
23+
'nested' => [
24+
'white' => '#ffffff',
25+
'variable' => 'var(--my-variable)',
26+
],
27+
'non-processable-item' => null,
28+
];
29+
30+
public static function renderDataProvider(): \Generator
31+
{
32+
yield 'value as argument' => [
33+
'<f:format.cssVariables value="{value}"/>',
34+
['value' => self::$value],
35+
'--red: #ff0000; --green: rgb(0, 255, 0); --nested-white: #ffffff; --nested-variable: var(--my-variable);',
36+
];
37+
yield 'value inline' => [
38+
'{value -> f:format.cssVariables()}',
39+
['value' => self::$value],
40+
'--red: #ff0000; --green: rgb(0, 255, 0); --nested-white: #ffffff; --nested-variable: var(--my-variable);',
41+
];
42+
yield 'value as child' => [
43+
'<f:format.cssVariables>{value}</f:format.cssVariables>',
44+
['value' => self::$value],
45+
'--red: #ff0000; --green: rgb(0, 255, 0); --nested-white: #ffffff; --nested-variable: var(--my-variable);',
46+
];
47+
yield 'value as child and argument' => [
48+
'<f:format.cssVariables value="{argument}">{child}</f:format.cssVariables>',
49+
['argument' => ['abc' => 'argument'], 'child' => ['abc' => 'child']],
50+
'--abc: argument;',
51+
];
52+
yield 'with prefix' => [
53+
'<f:format.cssVariables value="{value}" prefix="color"/>',
54+
['value' => self::$value],
55+
'--color-red: #ff0000; --color-green: rgb(0, 255, 0); --color-nested-white: #ffffff; --color-nested-variable: var(--my-variable);',
56+
];
57+
yield 'with selector' => [
58+
'<f:format.cssVariables value="{value}" selector=".my-css-class"/>',
59+
['value' => self::$value],
60+
'.my-css-class {
61+
--red: #ff0000;
62+
--green: rgb(0, 255, 0);
63+
--nested-white: #ffffff;
64+
--nested-variable: var(--my-variable);
65+
}',
66+
];
67+
yield 'with prefix and selector' => [
68+
'<f:format.cssVariables value="{value}" prefix="color" selector=".my-css-class, #my-id"/>',
69+
['value' => self::$value],
70+
'.my-css-class, #my-id {
71+
--color-red: #ff0000;
72+
--color-green: rgb(0, 255, 0);
73+
--color-nested-white: #ffffff;
74+
--color-nested-variable: var(--my-variable);
75+
}',
76+
];
77+
yield 'numeric array as value' => [
78+
'<f:format.cssVariables value="{value}" selector=":root"/>',
79+
['value' => ['foo', null, 'bar']],
80+
':root {
81+
--0: foo;
82+
--2: bar;
83+
}',
84+
];
85+
yield 'object as value' => [
86+
'<f:format.cssVariables value="{value}" selector=":root"/>',
87+
['value' => new IterableExample(self::$value)],
88+
':root {
89+
--red: #ff0000;
90+
--green: rgb(0, 255, 0);
91+
--nested-white: #ffffff;
92+
--nested-variable: var(--my-variable);
93+
}',
94+
];
95+
}
96+
97+
#[DataProvider('renderDataProvider')]
98+
#[Test]
99+
public function render(string $template, array $variables, $expected): void
100+
{
101+
$view = new TemplateView();
102+
$view->assignMultiple($variables);
103+
$view->getRenderingContext()->setCache(self::$cache);
104+
$view->getRenderingContext()->getTemplatePaths()->setTemplateSource($template);
105+
self::assertSame($expected, $view->render());
106+
107+
$view = new TemplateView();
108+
$view->assignMultiple($variables);
109+
$view->getRenderingContext()->setCache(self::$cache);
110+
$view->getRenderingContext()->getTemplatePaths()->setTemplateSource($template);
111+
self::assertSame($expected, $view->render());
112+
}
113+
}

0 commit comments

Comments
 (0)