From 77492752c40de3aa5ee3d28f1885a12cee21b9b8 Mon Sep 17 00:00:00 2001 From: tiamak Date: Tue, 14 Apr 2026 08:10:37 +0000 Subject: [PATCH] Limit menu sprite to third-level category icons --- advancedmegamenu.php | 39 ++++++++++++++----- src/Classes/SpriteGenerator.php | 64 +++++++++++++++++++++++++++++-- views/css/megamenu.css | 30 +++++++++++++++ views/templates/hook/megamenu.tpl | 14 +++++-- 4 files changed, 132 insertions(+), 15 deletions(-) diff --git a/advancedmegamenu.php b/advancedmegamenu.php index d70c1c4..ddff981 100644 --- a/advancedmegamenu.php +++ b/advancedmegamenu.php @@ -555,11 +555,21 @@ class AdvancedMegaMenu extends Module implements WidgetInterface private function generateSpriteAssets(int $idShop): void { $icons = Db::getInstance()->executeS( - 'SELECT `id_advmegamenu_item` AS `id`, `icon_path` - FROM `' . _DB_PREFIX_ . 'advmegamenu_item` - WHERE `id_shop` = ' . (int) $idShop . ' - AND `active` = 1 - AND `icon_path` != ""' + 'SELECT item.`id_advmegamenu_item` AS `id`, item.`icon_path` + FROM `' . _DB_PREFIX_ . 'advmegamenu_item` item + INNER JOIN `' . _DB_PREFIX_ . 'advmegamenu_item` parent + ON parent.`id_advmegamenu_item` = item.`parent_id` + AND parent.`id_shop` = item.`id_shop` + INNER JOIN `' . _DB_PREFIX_ . 'advmegamenu_item` grandparent + ON grandparent.`id_advmegamenu_item` = parent.`parent_id` + AND grandparent.`id_shop` = item.`id_shop` + WHERE item.`id_shop` = ' . (int) $idShop . ' + AND item.`active` = 1 + AND item.`type` = "category" + AND item.`icon_path` != "" + AND parent.`parent_id` > 0 + AND grandparent.`parent_id` = 0 + ORDER BY item.`position` ASC, item.`id_advmegamenu_item` ASC' ) ?: []; $generator = new SpriteGenerator($this->getLocalPath(), $this->getPathUri()); @@ -576,13 +586,14 @@ class AdvancedMegaMenu extends Module implements WidgetInterface * * @return array */ - private function buildFrontNode(array $node): array + private function buildFrontNode(array $node, int $depth = 0): array { $title = (string) ($node['title'] ?? ''); $description = (string) ($node['description'] ?? ''); $entityId = (int) ($node['entity_id'] ?? 0); $itemId = (int) ($node['id'] ?? 0); $type = (string) ($node['type'] ?? 'link'); + $usesSprite = $this->isThirdLevelCategoryNode($node, $depth); $frontNode = [ 'id' => $itemId, @@ -595,10 +606,12 @@ class AdvancedMegaMenu extends Module implements WidgetInterface 'current' => false, 'new_window' => !empty($node['new_window']), 'page_identifier' => $this->resolvePageIdentifier($type, $entityId, $node), - 'icon_class' => $node['icon_path'] ? 'adv-megamenu__icon adv-icon-item-' . $itemId : '', + 'icon_class' => $usesSprite && !empty($node['icon_path']) ? 'adv-megamenu__icon adv-icon-item-' . $itemId : '', 'icon_url' => (string) ($node['icon_url'] ?? ''), - 'children' => array_values(array_filter(array_map(function (array $child): array { - return $this->buildFrontNode($child); + 'uses_sprite' => $usesSprite, + 'depth' => $depth, + 'children' => array_values(array_filter(array_map(function (array $child) use ($depth): array { + return $this->buildFrontNode($child, $depth + 1); }, $node['children'] ?? []), static function (array $child): bool { return $child['active']; })), @@ -609,6 +622,14 @@ class AdvancedMegaMenu extends Module implements WidgetInterface return $frontNode; } + /** + * @param array $node + */ + private function isThirdLevelCategoryNode(array $node, int $depth): bool + { + return $depth === 2 && (string) ($node['type'] ?? '') === 'category'; + } + /** * @param array $node * diff --git a/src/Classes/SpriteGenerator.php b/src/Classes/SpriteGenerator.php index 1b9cbcb..6469e51 100644 --- a/src/Classes/SpriteGenerator.php +++ b/src/Classes/SpriteGenerator.php @@ -4,6 +4,8 @@ namespace AdvancedMegaMenu\Classes; class SpriteGenerator { + private const ICON_SIZE = 112; + private string $moduleDir; private string $moduleUri; @@ -52,12 +54,19 @@ class SpriteGenerator continue; } - $width = imagesx($image); - $height = imagesy($image); + $normalizedImage = $this->normalizeIconResource($image, self::ICON_SIZE); + imagedestroy($image); + + if (!$normalizedImage) { + continue; + } + + $width = imagesx($normalizedImage); + $height = imagesy($normalizedImage); $images[] = [ 'id' => (int) $icon['id'], - 'resource' => $image, + 'resource' => $normalizedImage, 'width' => $width, 'height' => $height, 'x' => $totalWidth, @@ -136,4 +145,53 @@ class SpriteGenerator return @imagecreatefromstring($contents); } + + /** + * @param resource $image + * + * @return resource|false + */ + private function normalizeIconResource($image, int $targetSize) + { + $sourceWidth = imagesx($image); + $sourceHeight = imagesy($image); + if ($sourceWidth <= 0 || $sourceHeight <= 0) { + return false; + } + + $scale = $targetSize / max($sourceWidth, $sourceHeight); + $targetWidth = max(1, (int) round($sourceWidth * $scale)); + $targetHeight = max(1, (int) round($sourceHeight * $scale)); + $destinationX = (int) floor(($targetSize - $targetWidth) / 2); + $destinationY = (int) floor(($targetSize - $targetHeight) / 2); + + $canvas = imagecreatetruecolor($targetSize, $targetSize); + if (!$canvas) { + return false; + } + + imagealphablending($canvas, false); + imagesavealpha($canvas, true); + $transparent = imagecolorallocatealpha($canvas, 0, 0, 0, 127); + imagefill($canvas, 0, 0, $transparent); + + if (!imagecopyresampled( + $canvas, + $image, + $destinationX, + $destinationY, + 0, + 0, + $targetWidth, + $targetHeight, + $sourceWidth, + $sourceHeight + )) { + imagedestroy($canvas); + + return false; + } + + return $canvas; + } } diff --git a/views/css/megamenu.css b/views/css/megamenu.css index ab99202..3494a0d 100644 --- a/views/css/megamenu.css +++ b/views/css/megamenu.css @@ -623,6 +623,19 @@ object-fit: cover; } +.adv-megamenu__mobile-thumb--sprite { + display: flex; + align-items: center; + justify-content: center; + padding: 0.5rem; +} + +.adv-megamenu__mobile-thumb--sprite .adv-megamenu__icon { + max-width: 100%; + max-height: 100%; + background-size: auto; +} + .adv-megamenu .adv-megamenu__mobile-link-row a, .adv-megamenu .adv-megamenu__mobile-link-row a:link, .adv-megamenu .adv-megamenu__mobile-link-row a:visited { @@ -738,6 +751,23 @@ background: #ffffff; } +.adv-megamenu__node-card-sprite { + display: flex; + align-items: center; + justify-content: center; + width: var(--adv-node-card-image-size); + height: var(--adv-node-card-image-size); + margin: 0 auto; + padding: 0.5rem; + background: #ffffff; +} + +.adv-megamenu__node-card-sprite .adv-megamenu__icon { + max-width: 100%; + max-height: 100%; + background-size: auto; +} + .adv-megamenu__node-card .card-body { display: flex; flex-direction: column; diff --git a/views/templates/hook/megamenu.tpl b/views/templates/hook/megamenu.tpl index 220310d..527a2d3 100644 --- a/views/templates/hook/megamenu.tpl +++ b/views/templates/hook/megamenu.tpl @@ -34,7 +34,11 @@ {foreach from=$nodes item=treeNode}
- {if $treeNode.icon_url} + {if $treeNode.uses_sprite && $treeNode.icon_class} +
+ +
+ {elseif $treeNode.icon_url} {$treeNode.title|escape:'htmlall':'UTF-8'} {/if}
@@ -180,8 +184,12 @@ {/foreach} {foreach from=$node.children item=child}
  • -