diff --git a/src/Twig/Functions/IconExtension.php b/src/Twig/Functions/IconExtension.php index 0a65dc6..cca3498 100644 --- a/src/Twig/Functions/IconExtension.php +++ b/src/Twig/Functions/IconExtension.php @@ -79,41 +79,20 @@ final class IconExtension extends AbstractExtension */ public function renderIcon(array $userOptions): string { - $options = $this->mergeUserOptionsWithDefaults($userOptions); + $options = $this->getMergedOptions($userOptions); + $svg = $this->getSanitisedIconSvg($options['icon']); + $colourClasses = $this->getColourClasses($options['colour'], $options['hover']); + $svg = $this->addClassesToSvg($svg, $colourClasses); + $svg = $this->addTitleToSvgIfNotNull($svg, $options['title']); + $svg = $this->setSvgHeightAndWidth($svg, $options['size']); - $this->throwIfUnrecognisedOptionExists($options); + return $svg; + } - $iconFilepath = $this->findSvgFilepath($options['icon']); - $rawSvgMarkup = $this->getSvgMarkup($iconFilepath); - $cleanSvgMarkup = $this->cleanSvgMarkup($rawSvgMarkup); - - $mainPallete = $this->getPallete($options['colour']); - - $colourClasses = "{$mainPallete['stroke']} {$mainPallete['fill']}"; - - if (null !== $options['hover']) { - $hoverPallete = $this->getPallete($options['hover']); - $colourClasses .= " cursor-pointer hover:{$hoverPallete['stroke']} hover:{$hoverPallete['fill']} group-hover:{$hoverPallete['stroke']} group-hover:{$hoverPallete['fill']}"; - } - - $xml = new \SimpleXMLElement($cleanSvgMarkup); - $xml = $this->addAttributeToXmlElement($xml, 'class', $colourClasses); - $markup = $this->removeXMLDeclaration($xml->saveXML()); - - if ($this->isNonEmptyString($options['title'])) - $markup = $this->addTitleToMarkup($markup, $options['title']); - - if ($options['size'] < 0) - throw new \InvalidArgumentException('Size must not be negative'); - - if (!is_int($options['size'])) - throw new \TypeError('Size value must be an integer'); - - $markup = $this->setSize($markup, $options['size']); - $markup = $this->removeBlackStrokeAttributes($markup); - $markup = $this->removeBlackFillAttributes($markup); - - return $markup; + private function getMergedOptions(array $userOptions): array + { + $this->throwIfUnrecognisedOptionExists($userOptions); + return $this->mergeUserOptionsWithDefaults($userOptions); } private function mergeUserOptionsWithDefaults(array $userOptions): array @@ -129,6 +108,15 @@ final class IconExtension extends AbstractExtension } } + private function getSanitisedIconSvg(string $iconName): string + { + $iconFilepath = $this->findSvgFilepath($iconName); + $rawSvgMarkup = $this->getSvgMarkup($iconFilepath); + $cleanSvgMarkup = $this->removeExistingTitleElement($rawSvgMarkup); + $cleanSvgMarkup = $this->removeBlackStrokeAttributes($cleanSvgMarkup); + return $this->removeBlackFillAttributes($cleanSvgMarkup); + } + private function findSvgFilepath(string $iconName): string { foreach ($this->directories as $directory) { @@ -146,9 +134,32 @@ final class IconExtension extends AbstractExtension return file_get_contents($filepath); } - private function cleanSvgMarkup(string $markup): string + private function removeExistingTitleElement(string $svg): string { - return preg_replace('/.*<\/title>/', '', $markup); + return preg_replace('/<title>.*<\/title>/', '', $svg); + } + + private function removeBlackStrokeAttributes(string $content): string + { + return preg_replace('/stroke(=|:)"?\s*(#0{6}|#000|rgb\(\s*0,\s*0,\s*0\s*\)|black)\s*"?/', '', $content); + } + + private function removeBlackFillAttributes(string $content): string + { + return preg_replace('/fill(=|:)"?\s*(#0{6}|#000|rgb\(\s*0,\s*0,\s*0\s*\)|black)\s*"?/', '', $content); + } + + private function getColourClasses(string $primaryColour, ?string $hoverColour): string + { + $mainPallete = $this->getPallete($primaryColour); + $colourClasses = "{$mainPallete['stroke']} {$mainPallete['fill']}"; + + if (null !== $hoverColour) { + $hoverPallete = $this->getPallete($hoverColour); + $colourClasses .= " cursor-pointer {$hoverPallete['stroke-hover']} {$hoverPallete['fill-hover']} {$hoverPallete['stroke-group-hover']} {$hoverPallete['fill-group-hover']}"; + } + + return $colourClasses; } private function getPallete(string $palleteName): array @@ -159,36 +170,19 @@ final class IconExtension extends AbstractExtension throw new PalleteNotFound("The pallete '$palleteName' was not found!"); } - private function isNonEmptyString(mixed $title): bool + private function addClassesToSvg(string $svg, string $classes): string { - if (!is_string($title) && null !== $title) - throw new \TypeError('Title must be a string!'); - - if ('' === $title) - throw new \InvalidArgumentException('Title string must not be empty!'); - - return true; + $xml = new \SimpleXMLElement($svg); + $xml = $this->addAttributeToXmlElement($xml, 'class', $classes); + return $this->removeXMLDeclaration($xml->saveXML()); } - private function addTitleToMarkup(string $markup, ?string $title): string + private function addTitleToSvgIfNotNull(string $svg, ?string $title): string { - if (null === $title) - return $markup; + if (null === $title) return $svg; + $this->throwIfTitleIsEmpty($title); - return preg_replace('/(<svg(.|\n)*?>\n?)/', "$1<title>$title", $markup); - } - - private function setSize(string $content, int $size): string - { - $svgAsXmlElement = new \SimpleXMLElement($content); - $svgAsXmlElement = $this->addAttributeToXmlElement($svgAsXmlElement, 'width', $size); - $svgAsXmlElement = $this->addAttributeToXmlElement($svgAsXmlElement, 'height', $size); - return $this->removeXMLDeclaration($svgAsXmlElement->saveXML()); - } - - private function removeXMLDeclaration(string $content): string - { - return trim(preg_replace('/<\?xml.*\?>/', '', $content)); + return preg_replace('/(\n?)/', "$1$title", $svg); } private function addAttributeToXmlElement(\SimpleXMLElement $xml, string $attrName, mixed $attrValue): \SimpleXMLElement @@ -202,14 +196,30 @@ final class IconExtension extends AbstractExtension return $xml; } - private function removeBlackStrokeAttributes(string $content): string + private function removeXMLDeclaration(string $content): string { - return preg_replace('/stroke(=|:)"?\s*(#0{6}|#000|rgb\(\s*0,\s*0,\s*0\s*\)|black)\s*"?/', '', $content); + return trim(preg_replace('/<\?xml.*\?>/', '', $content)); } - private function removeBlackFillAttributes(string $content): string + private function throwIfTitleIsEmpty(string $title): void { - return preg_replace('/fill(=|:)"?\s*(#0{6}|#000|rgb\(\s*0,\s*0,\s*0\s*\)|black)\s*"?/', '', $content); + if ('' === $title) + throw new \InvalidArgumentException('Title string must not be empty!'); + } + + private function setSvgHeightAndWidth(string $content, int $size): string + { + $this->throwIfSizeIsNegative($size); + $svgAsXmlElement = new \SimpleXMLElement($content); + $svgAsXmlElement = $this->addAttributeToXmlElement($svgAsXmlElement, 'width', $size); + $svgAsXmlElement = $this->addAttributeToXmlElement($svgAsXmlElement, 'height', $size); + return $this->removeXMLDeclaration($svgAsXmlElement->saveXML()); + } + + private function throwIfSizeIsNegative(int $size): void + { + if ($size < 0) + throw new \InvalidArgumentException('Size must not be negative'); } }