1<?php 2 3declare(strict_types=1); 4 5/* 6 * This file is part of the league/commonmark package. 7 * 8 * (c) Colin O'Dell <colinodell@gmail.com> 9 * 10 * For the full copyright and license information, please view the LICENSE 11 * file that was distributed with this source code. 12 */ 13 14namespace League\CommonMark\Extension\HeadingPermalink; 15 16use League\CommonMark\Node\Node; 17use League\CommonMark\Renderer\ChildNodeRendererInterface; 18use League\CommonMark\Renderer\NodeRendererInterface; 19use League\CommonMark\Util\HtmlElement; 20use League\CommonMark\Xml\XmlNodeRendererInterface; 21use League\Config\ConfigurationAwareInterface; 22use League\Config\ConfigurationInterface; 23 24/** 25 * Renders the HeadingPermalink elements 26 */ 27final class HeadingPermalinkRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface 28{ 29 public const DEFAULT_SYMBOL = '¶'; 30 31 /** @psalm-readonly-allow-private-mutation */ 32 private ConfigurationInterface $config; 33 34 public function setConfiguration(ConfigurationInterface $configuration): void 35 { 36 $this->config = $configuration; 37 } 38 39 /** 40 * @param HeadingPermalink $node 41 * 42 * {@inheritDoc} 43 * 44 * @psalm-suppress MoreSpecificImplementedParamType 45 */ 46 public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable 47 { 48 HeadingPermalink::assertInstanceOf($node); 49 50 $slug = $node->getSlug(); 51 52 $fragmentPrefix = (string) $this->config->get('heading_permalink/fragment_prefix'); 53 if ($fragmentPrefix !== '') { 54 $fragmentPrefix .= '-'; 55 } 56 57 $attrs = $node->data->getData('attributes'); 58 $appendId = ! $this->config->get('heading_permalink/apply_id_to_heading'); 59 60 if ($appendId) { 61 $idPrefix = (string) $this->config->get('heading_permalink/id_prefix'); 62 63 if ($idPrefix !== '') { 64 $idPrefix .= '-'; 65 } 66 67 $attrs->set('id', $idPrefix . $slug); 68 } 69 70 $attrs->set('href', '#' . $fragmentPrefix . $slug); 71 $attrs->append('class', $this->config->get('heading_permalink/html_class')); 72 73 $hidden = $this->config->get('heading_permalink/aria_hidden'); 74 if ($hidden) { 75 $attrs->set('aria-hidden', 'true'); 76 } 77 78 $attrs->set('title', $this->config->get('heading_permalink/title')); 79 80 $symbol = $this->config->get('heading_permalink/symbol'); 81 \assert(\is_string($symbol)); 82 83 return new HtmlElement('a', $attrs->export(), \htmlspecialchars($symbol), false); 84 } 85 86 public function getXmlTagName(Node $node): string 87 { 88 return 'heading_permalink'; 89 } 90 91 /** 92 * @param HeadingPermalink $node 93 * 94 * @return array<string, scalar> 95 * 96 * @psalm-suppress MoreSpecificImplementedParamType 97 */ 98 public function getXmlAttributes(Node $node): array 99 { 100 HeadingPermalink::assertInstanceOf($node); 101 102 return [ 103 'slug' => $node->getSlug(), 104 ]; 105 } 106} 107