| @@ -10,39 +10,37 @@ | ||
| 10 | 10 | |
| 11 | 11 | public function __construct(AIBUI_Translation_Manager $manager) |
| 12 | 12 | { |
| 13 | 13 | $this->manager = $manager; |
| 14 | - | |
| 15 | - add_action('wp_footer', array($this, 'render_switcher'), 20); | |
| 16 | 14 | } |
| 17 | 15 | |
| 18 | - public function render_switcher() | |
| 16 | + /** | |
| 17 | + * Render callback for the Language Switcher block. | |
| 18 | + */ | |
| 19 | + public function render_block($attributes = array(), $content = '', $block = null) | |
| 19 | 20 | { |
| 20 | 21 | if (!$this->manager->is_switcher_enabled()) { |
| 21 | - return; | |
| 22 | + return ''; | |
| 22 | 23 | } |
| 23 | 24 | |
| 24 | - $languages = $this->manager->get_available_languages(); | |
| 25 | + $languages = $this->get_ordered_languages(); | |
| 25 | 26 | if (count($languages) <= 1) { |
| 26 | - return; | |
| 27 | + return ''; | |
| 27 | 28 | } |
| 28 | 29 | |
| 29 | - $current = $this->manager->get_current_language(); | |
| 30 | - $supported = AIBUI_Translation_Handler::SUPPORTED_LANGUAGES; | |
| 31 | - $settings = AIBUI_Translation_Settings::get_settings(); | |
| 32 | - $bg_color = !empty($settings['switcher_bg']) ? $settings['switcher_bg'] : '#111827'; | |
| 33 | - $text_color = !empty($settings['switcher_text']) ? $settings['switcher_text'] : '#ffffff'; | |
| 34 | - $min_height = !empty($settings['switcher_min_height']) ? (int) $settings['switcher_min_height'] : 32; | |
| 35 | - $min_width = !empty($settings['switcher_min_width']) ? (int) $settings['switcher_min_width'] : 140; | |
| 36 | - $alpha = isset($settings['switcher_alpha']) ? (float) $settings['switcher_alpha'] : 0.9; | |
| 37 | - if ($alpha < 0) { | |
| 38 | - $alpha = 0; | |
| 39 | - } elseif ($alpha > 1) { | |
| 40 | - $alpha = 1; | |
| 41 | - } | |
| 30 | + $settings = AIBUI_Translation_Settings::get_settings(); | |
| 31 | + $supported = AIBUI_Translation_Handler::SUPPORTED_LANGUAGES; | |
| 32 | + $current = $this->manager->get_current_language(); | |
| 33 | + $select_id = 'aibui-lang-select-' . wp_generate_uuid4(); | |
| 42 | 34 | |
| 43 | - // Convert hex to rgba with configurable alpha for transparency. | |
| 44 | - $bg_rgba = sprintf('rgba(17, 24, 39, %.2f)', $alpha); // sensible default | |
| 35 | + $bg_color = !empty($settings['switcher_bg']) ? $settings['switcher_bg'] : '#5686c9'; | |
| 36 | + $text_color = !empty($settings['switcher_text']) ? $settings['switcher_text'] : '#ffffff'; | |
| 37 | + $min_height = !empty($settings['switcher_min_height']) ? (int) $settings['switcher_min_height'] : 30; | |
| 38 | + $min_width = !empty($settings['switcher_min_width']) ? (int) $settings['switcher_min_width'] : 90; | |
| 39 | + $alpha = isset($settings['switcher_alpha']) ? (float) $settings['switcher_alpha'] : 0.9; | |
| 40 | + $alpha = max(0, min(1, $alpha)); | |
| 41 | + | |
| 42 | + $bg_rgba = sprintf('rgba(17, 24, 39, %.2f)', $alpha); | |
| 45 | 43 | if (preg_match('/^#([0-9a-fA-F]{6})$/', $bg_color, $m)) { |
| 46 | 44 | $hex = $m[1]; |
| 47 | 45 | $r = hexdec(substr($hex, 0, 2)); |
| 48 | 46 | $g = hexdec(substr($hex, 2, 2)); |
| @@ -49,14 +47,57 @@ | ||
| 49 | 47 | $b = hexdec(substr($hex, 4, 2)); |
| 50 | 48 | $bg_rgba = sprintf('rgba(%d, %d, %d, %.2f)', $r, $g, $b, $alpha); |
| 51 | 49 | } |
| 52 | 50 | |
| 53 | - $current_url = $this->get_current_url(); | |
| 51 | + $attr_bg = isset($attributes['backgroundColor']) && $attributes['backgroundColor'] !== '' | |
| 52 | + ? sanitize_text_field($attributes['backgroundColor']) | |
| 53 | + : ''; | |
| 54 | + $attr_text = isset($attributes['textColor']) && $attributes['textColor'] !== '' | |
| 55 | + ? sanitize_text_field($attributes['textColor']) | |
| 56 | + : ''; | |
| 57 | + $attr_min_width = isset($attributes['minWidth']) && is_numeric($attributes['minWidth']) | |
| 58 | + ? (int) $attributes['minWidth'] | |
| 59 | + : null; | |
| 60 | + $attr_min_height = isset($attributes['minHeight']) && is_numeric($attributes['minHeight']) | |
| 61 | + ? (int) $attributes['minHeight'] | |
| 62 | + : null; | |
| 63 | + $default_radius = 22; | |
| 64 | + $attr_radius = isset($attributes['borderRadius']) && is_numeric($attributes['borderRadius']) | |
| 65 | + ? (int) $attributes['borderRadius'] | |
| 66 | + : null; | |
| 67 | + | |
| 68 | + $style_vars = array( | |
| 69 | + '--aibui-switcher-bg' => $attr_bg !== '' ? $attr_bg : $bg_rgba, | |
| 70 | + '--aibui-switcher-text' => $attr_text !== '' ? $attr_text : $text_color, | |
| 71 | + '--aibui-switcher-min-height' => ($attr_min_height !== null ? $attr_min_height : $min_height) . 'px', | |
| 72 | + '--aibui-switcher-min-width' => ($attr_min_width !== null ? $attr_min_width : $min_width) . 'px', | |
| 73 | + '--aibui-switcher-radius' => ($attr_radius !== null ? $attr_radius : $default_radius) . 'px', | |
| 74 | + ); | |
| 75 | + | |
| 76 | + $style_attr = ''; | |
| 77 | + foreach ($style_vars as $var => $value) { | |
| 78 | + if ($value !== '' && $value !== null) { | |
| 79 | + $style_attr .= $var . ':' . $value . ';'; | |
| 80 | + } | |
| 81 | + } | |
| 82 | + | |
| 83 | + $classes = 'aibui-lang-switcher-block aibui-lang-switcher-block--no-label'; | |
| 84 | + | |
| 85 | + $wrapper_attributes = function_exists('get_block_wrapper_attributes') | |
| 86 | + ? get_block_wrapper_attributes(array( | |
| 87 | + 'class' => trim($classes), | |
| 88 | + 'style' => $style_attr, | |
| 89 | + )) | |
| 90 | + : sprintf('class="%s" style="%s"', esc_attr(trim($classes)), esc_attr($style_attr)); | |
| 91 | + | |
| 92 | + ob_start(); | |
| 54 | 93 | ?> |
| 55 | - <div class="aibui-lang-switcher"> | |
| 94 | + <div <?php echo $wrapper_attributes; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> | |
| 56 | 95 | <form method="get" class="aibui-lang-switcher__form"> |
| 57 | - <label for="aibui-lang-select" class="screen-reader-text"><?php esc_html_e('Select language', 'ai-builder'); ?></label> | |
| 58 | - <select id="aibui-lang-select" name="ai_lang" onchange="this.form.submit()"> | |
| 96 | + <label for="<?php echo esc_attr($select_id); ?>" class="screen-reader-text"> | |
| 97 | + <?php esc_html_e('Select language', 'ai-builder'); ?> | |
| 98 | + </label> | |
| 99 | + <select id="<?php echo esc_attr($select_id); ?>" name="ai_lang" onchange="this.form.submit()"> | |
| 59 | 100 | <?php foreach ($languages as $lang_code) : |
| 60 | 101 | $label = $supported[$lang_code] ?? strtoupper($lang_code); |
| 61 | 102 | ?> |
| 62 | 103 | <option value="<?php echo esc_attr($lang_code); ?>" <?php selected($current, $lang_code); ?>> |
| @@ -63,97 +104,44 @@ | ||
| 63 | 104 | <?php echo esc_html($label); ?> |
| 64 | 105 | </option> |
| 65 | 106 | <?php endforeach; ?> |
| 66 | 107 | </select> |
| 67 | - <?php | |
| 68 | - // Preserve other query vars | |
| 69 | - $query = $_GET; | |
| 70 | - unset($query['ai_lang']); | |
| 71 | - foreach ($query as $key => $value) { | |
| 72 | - if (is_array($value)) { | |
| 73 | - foreach ($value as $val) { | |
| 74 | - printf('<input type="hidden" name="%s[]" value="%s" />', esc_attr($key), esc_attr($val)); | |
| 75 | - } | |
| 76 | - } else { | |
| 77 | - printf('<input type="hidden" name="%s" value="%s" />', esc_attr($key), esc_attr($value)); | |
| 78 | - } | |
| 79 | - } | |
| 80 | - ?> | |
| 108 | + <?php $this->render_preserved_query_fields(); ?> | |
| 81 | 109 | </form> |
| 82 | 110 | </div> |
| 83 | - <style> | |
| 84 | - .aibui-lang-switcher { | |
| 85 | - position: fixed; | |
| 86 | - top: 0; | |
| 87 | - right: 0; | |
| 88 | - background: <?php echo esc_attr($bg_rgba); ?>; | |
| 89 | - padding: 0px 14px 10px 14px; | |
| 90 | - border-radius: 0 0 0 16px; /* only bottom-left corner rounded */ | |
| 91 | - z-index: 9999; | |
| 92 | - min-height: <?php echo (int) $min_height; ?>px; | |
| 93 | - min-width: <?php echo (int) $min_width; ?>px; | |
| 94 | - display: flex; | |
| 95 | - align-items: center; | |
| 96 | - justify-content: center; | |
| 97 | - cursor: pointer; | |
| 98 | - } | |
| 111 | + <?php | |
| 112 | + return ob_get_clean(); | |
| 113 | + } | |
| 99 | 114 | |
| 100 | - /* When logged-in admin bar is visible, offset the switcher */ | |
| 101 | - body.admin-bar .aibui-lang-switcher { | |
| 102 | - top: 32px; | |
| 103 | - } | |
| 115 | + private function get_ordered_languages() | |
| 116 | + { | |
| 117 | + $available = $this->manager->get_available_languages(); | |
| 118 | + $default = $this->manager->get_default_language(); | |
| 104 | 119 | |
| 105 | - @media screen and (max-width: 782px) { | |
| 106 | - body.admin-bar .aibui-lang-switcher { | |
| 107 | - top: 46px; | |
| 108 | - } | |
| 109 | - } | |
| 120 | + $available = array_values(array_unique(array_filter((array) $available))); | |
| 110 | 121 | |
| 111 | - .aibui-lang-switcher select { | |
| 112 | - background: transparent; | |
| 113 | - color: <?php echo esc_attr($text_color); ?>; | |
| 114 | - border: none; | |
| 115 | - border-radius: 0; | |
| 116 | - padding: 4px 8px; | |
| 117 | - font-weight: 600; | |
| 118 | - outline: none; | |
| 119 | - box-shadow: none; | |
| 120 | - cursor: pointer; | |
| 121 | - } | |
| 122 | + if ($default && in_array($default, $available, true)) { | |
| 123 | + $available = array_values(array_diff($available, array($default))); | |
| 124 | + array_unshift($available, $default); | |
| 125 | + } elseif ($default) { | |
| 126 | + array_unshift($available, $default); | |
| 127 | + } | |
| 122 | 128 | |
| 123 | - .aibui-lang-switcher select option { | |
| 124 | - color: #000; | |
| 125 | - } | |
| 126 | - </style> | |
| 127 | - <script> | |
| 128 | - (function(){ | |
| 129 | - var wrapper = document.querySelector('.aibui-lang-switcher'); | |
| 130 | - var select = document.getElementById('aibui-lang-select'); | |
| 131 | - if (wrapper && select) { | |
| 132 | - wrapper.addEventListener('click', function(e) { | |
| 133 | - if (e.target !== select) { | |
| 134 | - // Programmatically open the select dropdown | |
| 135 | - if (typeof select.showPicker === 'function') { | |
| 136 | - select.showPicker(); | |
| 137 | - } else { | |
| 138 | - // Fallback: focus and simulate mousedown for older browsers | |
| 139 | - select.focus(); | |
| 140 | - var evt = new MouseEvent('mousedown', { bubbles: true, cancelable: true, view: window }); | |
| 141 | - select.dispatchEvent(evt); | |
| 142 | - } | |
| 143 | - } | |
| 144 | - }); | |
| 145 | - } | |
| 146 | - })(); | |
| 147 | - </script> | |
| 148 | - <?php | |
| 129 | + return $available; | |
| 149 | 130 | } |
| 150 | 131 | |
| 151 | - private function get_current_url() | |
| 132 | + private function render_preserved_query_fields() | |
| 152 | 133 | { |
| 153 | - $scheme = is_ssl() ? 'https' : 'http'; | |
| 154 | - $host = $_SERVER['HTTP_HOST'] ?? ''; | |
| 155 | - $uri = $_SERVER['REQUEST_URI'] ?? ''; | |
| 156 | - return $scheme . '://' . $host . $uri; | |
| 134 | + $query = $_GET; | |
| 135 | + unset($query['ai_lang']); | |
| 136 | + foreach ($query as $key => $value) { | |
| 137 | + if (is_array($value)) { | |
| 138 | + foreach ($value as $val) { | |
| 139 | + printf('<input type="hidden" name="%s[]" value="%s" />', esc_attr($key), esc_attr(wp_unslash($val))); | |
| 140 | + } | |
| 141 | + } else { | |
| 142 | + printf('<input type="hidden" name="%s" value="%s" />', esc_attr($key), esc_attr(wp_unslash($value))); | |
| 143 | + } | |
| 144 | + } | |
| 157 | 145 | } |
| 158 | 146 | } |
| 159 | 147 | |