Skip to content

Commit 03a5de1

Browse files
committed
Add an attr function to make outputting HTML attributes easier
1 parent b93eb3c commit 03a5de1

File tree

1 file changed

+79
-0
lines changed

1 file changed

+79
-0
lines changed

extra/html-extra/HtmlExtension.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@
1212
namespace Twig\Extra\Html;
1313

1414
use Symfony\Component\Mime\MimeTypes;
15+
use Twig\Environment;
1516
use Twig\Error\RuntimeError;
1617
use Twig\Extension\AbstractExtension;
18+
use Twig\Extension\CoreExtension;
19+
use Twig\Extension\EscaperExtension;
20+
use Twig\Runtime\EscaperRuntime;
1721
use Twig\TwigFilter;
1822
use Twig\TwigFunction;
1923

@@ -30,6 +34,7 @@ public function getFilters(): array
3034
{
3135
return [
3236
new TwigFilter('data_uri', [$this, 'dataUri']),
37+
new TwigFilter('html_attr_merge', [self::class, 'htmlAttrMerge']),
3338
];
3439
}
3540

@@ -38,6 +43,7 @@ public function getFunctions(): array
3843
return [
3944
new TwigFunction('html_classes', [self::class, 'htmlClasses']),
4045
new TwigFunction('html_cva', [self::class, 'htmlCva']),
46+
new TwigFunction('html_attr', [self::class, 'htmlAttr'], ['needs_environment' => true, 'is_safe' => ['html']]),
4147
];
4248
}
4349

@@ -124,4 +130,77 @@ public static function htmlCva(array|string $base = [], array $variants = [], ar
124130
{
125131
return new Cva($base, $variants, $compoundVariants, $defaultVariant);
126132
}
133+
134+
public static function htmlAttrMerge(...$arrays): array
135+
{
136+
$result = [];
137+
138+
foreach ($arrays as $argNumber => $array) {
139+
if (!$array) {
140+
continue;
141+
}
142+
143+
if (!is_iterable($array)) {
144+
throw new RuntimeError(sprintf('The "attr_merge" filter only works with arrays or "Traversable", got "%s" for argument %d.', \gettype($array), $argNumber + 1));
145+
}
146+
147+
$array = CoreExtension::toArray($array);
148+
149+
foreach (['class', 'style', 'data', 'aria'] as $deepMergeKey) {
150+
if (isset($array[$deepMergeKey])) {
151+
$value = $array[$deepMergeKey];
152+
unset($array[$deepMergeKey]);
153+
154+
if (!is_iterable($value)) {
155+
$value = (array) $value;
156+
}
157+
158+
$value = CoreExtension::toArray($value);
159+
160+
$result[$deepMergeKey] = array_merge($result[$deepMergeKey] ?? [], $value);
161+
}
162+
}
163+
164+
$result = array_merge($result, $array);
165+
}
166+
167+
return $result;
168+
}
169+
170+
public static function htmlAttr(Environment $env, ...$args): string
171+
{
172+
$attr = self::htmlAttrMerge(...$args);
173+
174+
if (isset($attr['class'])) {
175+
$attr['class'] = trim(implode(' ', $attr['class']));
176+
}
177+
178+
if (isset($attr['style'])) {
179+
$style = '';
180+
foreach ($attr['style'] as $name => $value) {
181+
if (is_numeric($name)) {
182+
$style .= $value.'; ';
183+
} else {
184+
$style .= $name.': '.$value.'; ';
185+
}
186+
}
187+
$attr['style'] = trim($style);
188+
}
189+
190+
if (isset($attr['data'])) {
191+
foreach ($attr['data'] as $name => $value) {
192+
$attr['data-'.$name] = $value;
193+
}
194+
unset($attr['data']);
195+
}
196+
197+
$result = '';
198+
$runtime = $env->getRuntime(EscaperRuntime::class);
199+
200+
foreach ($attr as $name => $value) {
201+
$result .= $runtime->escape($name, 'html_attr').'="'.$runtime->escape($value).'" ';
202+
}
203+
204+
return trim($result);
205+
}
127206
}

0 commit comments

Comments
 (0)