-
-
Notifications
You must be signed in to change notification settings - Fork 112
Closed
Description
I'd love to see an official n:tagname macro that allows changing the html tag name of the current element.
This is useful in two scenarios:
- Semantical & accessible applications that require differing heading levels (h1, h2, h3) throughout the app
- Markup requirements where just removing an element via
n:tag-ifis too much and you'd rather turn thatainto aspandepending on some variable.
If you agree this is a useful feature, see the code below for a working implementation.
Example
{* title.latte *}
{default $heading = null}
<h2 n:tagname="$heading">
{$page->title}
</h2>This will create an h2:
{include 'title.latte'}This will create an h3:
{include 'title.latte', heading => h3}Prior art
Vue components have this behavior; the macro there is called is which is nice and concise:
<!-- Render a <table> tag -->
<component is="table"></component>While the is attr is well-known and unambigious, I feel that tagName would be more obvious for quick scanning of the source code.
Implementation
I have implemented this behavior for a project before. See below. Might serve as inspiration or starting point.
/**
* n:tagname
*/
public function macroTagname(MacroNode $node, PhpWriter $writer)
{
if (!$node->prefix || $node->prefix !== MacroNode::PREFIX_NONE) {
throw new CompileException("Unknown {$node->getNotation()}, use n:{$node->name} attribute.");
}
if (!$node->args) {
throw new CompileException("The n:{$node->name} value cannot be empty.");
}
}
/**
* n:tagname
*
*/
public function macroTagnameEnd(MacroNode $node, PhpWriter $writer)
{
$exception = CompileException::class;
$node->openingCode = '<?php
$__tagMacro = "' . $node->name . '";
$__tagNameOld = "' . $node->htmlNode->name . '";
$__tagNameNew = ' . $writer->formatArgs() . ';
// Enforce valid tag name if defined
if ($__tagNameNew !== null) {
if (!is_string($__tagNameNew)) {
throw new ' . $exception . '("The tag name passed to n:{$__tagMacro} must be a string.");
}
$__tagNameNew = trim($__tagNameNew);
if (!$__tagNameNew) {
throw new ' . $exception . '("The tag name passed to n:{$__tagMacro} must not be empty.");
}
try {
new \DOMElement($__tagNameNew);
} catch (\DOMException $e) {
throw new ' . $exception . '("Invalid tag name supplied to n:{$__tagMacro}: {$__tagNameNew}.");
}
}
ob_start(function () {});
?>';
$node->closingCode = '<?php
$__tagContent = ob_get_clean();
if ($__tagNameNew && $__tagNameOld !== $__tagNameNew) {
// Replace starting tag
$__tagContent = preg_replace(
"/^(\s*)<" . $__tagNameOld . "\b/",
"$1<" . $__tagNameNew,
$__tagContent
);
// Replace end tag
$__tagContent = preg_replace(
"/<\/" . $__tagNameOld . ">(\s*)$/",
"</" . $__tagNameNew . ">$1",
$__tagContent
);
}
echo $__tagContent;
?>';
}Metadata
Metadata
Assignees
Labels
No labels