diff --git a/CHANGELOG.md b/CHANGELOG.md index bf294c8..b38b582 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,11 @@ ## [Unreleased] +### Added +- Collapsible floating social bar with toggle button and localStorage persistence +- Bootstrap tooltips on all social icon links showing platform name on hover +- URL validation on social platform fields (browser, Joomla server-side, and PHP layout logging) + ### Fixed - Moved footer social icons below footer-menu and footer module positions for correct display order diff --git a/source/html/layouts/mokoonyx/social-icons.php b/source/html/layouts/mokoonyx/social-icons.php index 7ba30d5..c575040 100644 --- a/source/html/layouts/mokoonyx/social-icons.php +++ b/source/html/layouts/mokoonyx/social-icons.php @@ -52,13 +52,22 @@ $platforms = [ $active = []; foreach ($platforms as $key => [$iconClass, $langKey]) { $url = trim((string) $params->get('social_' . $key . '_url', '')); - if ($url !== '' && preg_match('#^(https?://|mailto:|/)#i', $url)) { - $active[] = [ - 'url' => $url, - 'iconClass' => $iconClass, - 'label' => Text::_($langKey), - ]; + if ($url === '') { + continue; } + if (!preg_match('#^(https?://[^\s<>"]+|mailto:[^\s<>"]+|/[^\s<>"]*)$#i', $url)) { + \Joomla\CMS\Log\Log::add( + 'MokoOnyx social: skipped invalid URL for "' . $key . '": ' . $url, + \Joomla\CMS\Log\Log::WARNING, + 'template' + ); + continue; + } + $active[] = [ + 'url' => $url, + 'iconClass' => $iconClass, + 'label' => Text::_($langKey), + ]; } if (empty($active)) { @@ -84,6 +93,10 @@ if ($position === 'floating') { $listClass .= ' moko-social-icons--floating-' . $floatingPos; } ?> + +
+ diff --git a/source/language/en-GB/tpl_mokoonyx.ini b/source/language/en-GB/tpl_mokoonyx.ini index 25e16e9..566a177 100644 --- a/source/language/en-GB/tpl_mokoonyx.ini +++ b/source/language/en-GB/tpl_mokoonyx.ini @@ -286,6 +286,7 @@ TPL_MOKOONYX_SOCIAL_FLOATING_POS_LABEL="Floating Position" TPL_MOKOONYX_SOCIAL_FLOATING_POS_DESC="Which side of the screen the floating social bar appears on." TPL_MOKOONYX_SOCIAL_FLOATING_POS_LEFT="Left" TPL_MOKOONYX_SOCIAL_FLOATING_POS_RIGHT="Right" +TPL_MOKOONYX_SOCIAL_FLOATING_TOGGLE="Toggle social icons sidebar" TPL_MOKOONYX_SOCIAL_COLOR_LABEL="Icon Colour" TPL_MOKOONYX_SOCIAL_COLOR_DESC="Choose the colour scheme for social icons." TPL_MOKOONYX_SOCIAL_COLOR_THEME="Theme (CSS variables)" @@ -321,7 +322,7 @@ TPL_MOKOONYX_SOCIAL_MAIL="Email us" ; ===== CSS Variables tab (social) ===== TPL_MOKOONYX_CSS_VARS_SOCIAL_LABEL="Social Icons" -TPL_MOKOONYX_CSS_VARS_SOCIAL_DESC="--social-icon-size — Icon font size (overrides size preset)--social-icon-gap — Gap between icons (default: 0.5rem)--social-icon-color — Icon colour (default: currentColor)--social-icon-hover-color — Hover colour (default: var(--accent-color-primary))--social-icon-bg — Background for circle/rounded styles--social-icon-hover-bg — Hover background--social-icon-radius — Border radius for rounded style (default: 0.375rem)"
+TPL_MOKOONYX_CSS_VARS_SOCIAL_DESC="--social-icon-size — Icon font size (overrides size preset)--social-icon-gap — Gap between icons (default: 0.5rem)--social-icon-color — Icon colour (default: currentColor)--social-icon-hover-color — Hover colour (default: var(--accent-color-primary))--social-icon-bg — Background for circle/rounded styles--social-icon-hover-bg — Hover background--social-icon-radius — Border radius for rounded style (default: 0.375rem)--social-icon-bg, --social-icon-color, and --social-icon-hover-bg. Tooltips appear automatically on hover showing the platform name."
; ===== Misc =====
MOD_BREADCRUMBS_HERE="YOU ARE HERE:"
diff --git a/source/language/en-US/tpl_mokoonyx.ini b/source/language/en-US/tpl_mokoonyx.ini
index 3e60211..a671025 100644
--- a/source/language/en-US/tpl_mokoonyx.ini
+++ b/source/language/en-US/tpl_mokoonyx.ini
@@ -286,6 +286,7 @@ TPL_MOKOONYX_SOCIAL_FLOATING_POS_LABEL="Floating Position"
TPL_MOKOONYX_SOCIAL_FLOATING_POS_DESC="Which side of the screen the floating social bar appears on."
TPL_MOKOONYX_SOCIAL_FLOATING_POS_LEFT="Left"
TPL_MOKOONYX_SOCIAL_FLOATING_POS_RIGHT="Right"
+TPL_MOKOONYX_SOCIAL_FLOATING_TOGGLE="Toggle social icons sidebar"
TPL_MOKOONYX_SOCIAL_COLOR_LABEL="Icon Color"
TPL_MOKOONYX_SOCIAL_COLOR_DESC="Choose the color scheme for social icons."
TPL_MOKOONYX_SOCIAL_COLOR_THEME="Theme (CSS variables)"
@@ -321,7 +322,7 @@ TPL_MOKOONYX_SOCIAL_MAIL="Email us"
; ===== CSS Variables tab (social) =====
TPL_MOKOONYX_CSS_VARS_SOCIAL_LABEL="Social Icons"
-TPL_MOKOONYX_CSS_VARS_SOCIAL_DESC="--social-icon-size — Icon font size (overrides size preset)--social-icon-gap — Gap between icons (default: 0.5rem)--social-icon-color — Icon color (default: currentColor)--social-icon-hover-color — Hover color (default: var(--accent-color-primary))--social-icon-bg — Background for circle/rounded styles--social-icon-hover-bg — Hover background--social-icon-radius — Border radius for rounded style (default: 0.375rem)"
+TPL_MOKOONYX_CSS_VARS_SOCIAL_DESC="--social-icon-size — Icon font size (overrides size preset)--social-icon-gap — Gap between icons (default: 0.5rem)--social-icon-color — Icon color (default: currentColor)--social-icon-hover-color — Hover color (default: var(--accent-color-primary))--social-icon-bg — Background for circle/rounded styles--social-icon-hover-bg — Hover background--social-icon-radius — Border radius for rounded style (default: 0.375rem)--social-icon-bg, --social-icon-color, and --social-icon-hover-bg. Tooltips appear automatically on hover showing the platform name."
; ===== Misc =====
MOD_BREADCRUMBS_HERE="YOU ARE HERE:"
diff --git a/source/media/css/template.css b/source/media/css/template.css
index 9b4762a..4924520 100644
--- a/source/media/css/template.css
+++ b/source/media/css/template.css
@@ -23679,26 +23679,42 @@ font-size: 0.8125rem;
}
/* Position: floating — fixed sidebar, hidden on mobile */
-.moko-social-icons--floating {
+.moko-social-floating-wrap {
position: fixed;
top: 50%;
transform: translateY(-50%);
z-index: 1030;
display: flex;
+ align-items: center;
+ transition: transform 0.3s ease;
+}
+
+.moko-social-floating-wrap--left {
+ left: 0;
+ flex-direction: row;
+}
+
+.moko-social-floating-wrap--right {
+ right: 0;
+ flex-direction: row-reverse;
+}
+
+.moko-social-floating-wrap--left.is-collapsed {
+ transform: translateY(-50%) translateX(calc(-100% + 1.5rem));
+}
+
+.moko-social-floating-wrap--right.is-collapsed {
+ transform: translateY(-50%) translateX(calc(100% - 1.5rem));
+}
+
+.moko-social-icons--floating {
+ display: flex;
}
.moko-social-icons--floating ul {
flex-direction: column;
}
-.moko-social-icons--floating-left {
- left: 0;
-}
-
-.moko-social-icons--floating-right {
- right: 0;
-}
-
.moko-social-icons--floating-left a {
border-radius: 0 var(--social-icon-radius, 0.375rem) var(--social-icon-radius, 0.375rem) 0;
}
@@ -23712,8 +23728,48 @@ font-size: 0.8125rem;
border-radius: inherit;
}
+/* Floating toggle button */
+.moko-social-floating-toggle {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 1.5rem;
+ height: 2.5rem;
+ padding: 0;
+ border: 1px solid var(--social-icon-bg, var(--border-color, hsl(0, 0%, 80%)));
+ background: var(--social-icon-bg, var(--body-bg, hsl(0, 0%, 100%)));
+ color: var(--social-icon-color, currentColor);
+ cursor: pointer;
+ font-size: 0.65rem;
+ transition: background 0.2s, color 0.2s;
+ flex-shrink: 0;
+}
+
+.moko-social-floating-wrap--left .moko-social-floating-toggle {
+ border-radius: 0 var(--social-icon-radius, 0.375rem) var(--social-icon-radius, 0.375rem) 0;
+ border-left: 0;
+}
+
+.moko-social-floating-wrap--right .moko-social-floating-toggle {
+ border-radius: var(--social-icon-radius, 0.375rem) 0 0 var(--social-icon-radius, 0.375rem);
+ border-right: 0;
+}
+
+.moko-social-floating-toggle:hover {
+ background: var(--social-icon-hover-bg, var(--accent-color-primary, hsl(220, 70%, 50%)));
+ color: #fff;
+}
+
+.moko-social-floating-toggle .fa-solid {
+ transition: transform 0.3s ease;
+}
+
+.is-collapsed .moko-social-floating-toggle .fa-solid {
+ transform: rotate(180deg);
+}
+
@media (max-width: 991.98px) {
- .moko-social-icons--floating {
+ .moko-social-floating-wrap {
display: none;
}
}
diff --git a/source/media/js/template.js b/source/media/js/template.js
index 70a2f6a..b650658 100644
--- a/source/media/js/template.js
+++ b/source/media/js/template.js
@@ -789,6 +789,46 @@
});
}
+ // ========================================================================
+ // FLOATING SOCIAL BAR (collapsible)
+ // ========================================================================
+ var socialStorageKey = "moko-social-collapsed";
+
+ function initSocialFloating() {
+ var wrap = doc.getElementById("mokoSocialFloating");
+ var toggle = doc.getElementById("mokoSocialFloatingToggle");
+ if (!wrap || !toggle) return;
+
+ // Restore saved state
+ try {
+ if (localStorage.getItem(socialStorageKey) === "1") {
+ wrap.classList.add("is-collapsed");
+ toggle.setAttribute("aria-expanded", "false");
+ }
+ } catch (e) {}
+
+ toggle.addEventListener("click", function () {
+ var collapsed = wrap.classList.toggle("is-collapsed");
+ toggle.setAttribute("aria-expanded", collapsed ? "false" : "true");
+ try { localStorage.setItem(socialStorageKey, collapsed ? "1" : "0"); } catch (e) {}
+ });
+ }
+
+ // ========================================================================
+ // SOCIAL ICON TOOLTIPS
+ // ========================================================================
+ function initSocialTooltips() {
+ var triggers = doc.querySelectorAll('.moko-social-icons [data-bs-toggle="tooltip"]');
+ if (!triggers.length) return;
+
+ // Bootstrap 5 tooltip init
+ if (typeof bootstrap !== "undefined" && bootstrap.Tooltip) {
+ triggers.forEach(function (el) {
+ new bootstrap.Tooltip(el, { trigger: "hover focus" });
+ });
+ }
+ }
+
/**
* Run all template JS initializations
*/
@@ -815,6 +855,8 @@
initBackTop();
initSearchToggle();
initSidebarAccordion();
+ initSocialFloating();
+ initSocialTooltips();
initVarCopy();
}
diff --git a/source/templateDetails.xml b/source/templateDetails.xml
index 441f3c9..bfdb28c 100644
--- a/source/templateDetails.xml
+++ b/source/templateDetails.xml
@@ -387,28 +387,28 @@
showon="social_topbar:1[OR]social_footer:1[OR]social_floating:1" />