| @@ -1,5 +1,10 @@ | ||
| 1 | 1 | <?php |
| 2 | +// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- view template receives variables via extract(); prefixing is impractical. | |
| 3 | + | |
| 4 | +if ( ! defined( 'ABSPATH' ) ) { | |
| 5 | + exit; | |
| 6 | +} | |
| 2 | 7 | /** |
| 3 | 8 | * Template part for BetterDocs Code Snippet |
| 4 | 9 | * |
| 5 | 10 | * @var string $code_content |
| @@ -5,9 +10,8 @@ | ||
| 5 | 10 | * @var string $code_content |
| 6 | 11 | * @var string $language |
| 7 | 12 | * @var bool $show_language_label |
| 8 | 13 | * @var bool $show_copy_button |
| 9 | - * @var bool $show_copy_tooltip | |
| 10 | 14 | * @var bool $show_header |
| 11 | 15 | * @var bool $show_line_numbers |
| 12 | 16 | * @var string $theme |
| 13 | 17 | * @var string $block_id (optional) |
| @@ -15,8 +19,10 @@ | ||
| 15 | 19 | * @var string $file_name (optional) |
| 16 | 20 | * @var bool $show_traffic_lights (optional) |
| 17 | 21 | * @var bool $show_file_icon (optional) |
| 18 | 22 | * @var string $file_icon (optional) |
| 23 | + * @var array $code_variants (optional) [ { language, code, file_name } ] — when | |
| 24 | + * more than one, the header renders a language dropdown. | |
| 19 | 25 | */ |
| 20 | 26 | |
| 21 | 27 | if ( empty( $code_content ) ) { |
| 22 | 28 | return; |
| @@ -25,11 +31,14 @@ | ||
| 25 | 31 | // Generate unique ID for this code snippet |
| 26 | 32 | $snippet_id = isset( $block_id ) ? $block_id : 'betterdocs-code-snippet-' . wp_rand( 1000, 9999 ); |
| 27 | 33 | |
| 28 | 34 | // Set defaults for optional parameters |
| 35 | +// Gates the language name on the multi-language switcher. The Elementor switch | |
| 36 | +// and the block's showLanguageLabel attribute both fed this variable and nothing | |
| 37 | +// ever read it, so the control was a no-op in both editors. Default stays true, | |
| 38 | +// so existing content renders exactly as before. | |
| 29 | 39 | $show_language_label = isset( $show_language_label ) ? $show_language_label : true; |
| 30 | 40 | $show_copy_button = isset( $show_copy_button ) ? $show_copy_button : true; |
| 31 | -$show_copy_tooltip = isset( $show_copy_tooltip ) ? $show_copy_tooltip : false; | |
| 32 | 41 | $show_header = isset( $show_header ) ? $show_header : true; |
| 33 | 42 | $file_name = isset( $file_name ) ? $file_name : ''; |
| 34 | 43 | $show_traffic_lights = isset( $show_traffic_lights ) ? $show_traffic_lights : true; |
| 35 | 44 | $show_file_icon = isset( $show_file_icon ) ? $show_file_icon : true; |
| @@ -43,23 +52,80 @@ | ||
| 43 | 52 | |
| 44 | 53 | // Import Helper class for file icon functionality |
| 45 | 54 | use WPDeveloper\BetterDocs\Utils\Helper; |
| 46 | 55 | |
| 47 | -// Generate line numbers if needed | |
| 48 | -$line_numbers = []; | |
| 49 | -if ( $show_line_numbers ) { | |
| 50 | - $lines = explode( "\n", $code_content ); | |
| 51 | - $line_numbers = range( 1, count( $lines ) ); | |
| 56 | +// Build the language list. Callers that don't pass code_variants (Elementor / | |
| 57 | +// shortcode) fall back to the single primary language/code — identical to the | |
| 58 | +// previous behaviour. | |
| 59 | +$variants = ( isset( $code_variants ) && is_array( $code_variants ) && ! empty( $code_variants ) ) | |
| 60 | + ? $code_variants | |
| 61 | + : [ | |
| 62 | + [ | |
| 63 | + 'language' => $language, | |
| 64 | + 'code' => $code_content, | |
| 65 | + 'file_name' => $file_name | |
| 66 | + ] | |
| 67 | + ]; | |
| 68 | + | |
| 69 | +// Normalize every entry up front. `code_variants` arrives from block attributes | |
| 70 | +// (and from Pro's API-docs generator), so a caller can legitimately hand over an | |
| 71 | +// entry missing `language` or `code` — reading them directly emitted PHP 8 | |
| 72 | +// warnings mid-render. The tab template already guards this way. | |
| 73 | +$variants = array_values( | |
| 74 | + array_filter( | |
| 75 | + array_map( | |
| 76 | + function ( $variant ) { | |
| 77 | + if ( ! is_array( $variant ) ) { | |
| 78 | + return null; | |
| 79 | + } | |
| 80 | + | |
| 81 | + return [ | |
| 82 | + 'language' => isset( $variant['language'] ) ? (string) $variant['language'] : '', | |
| 83 | + 'code' => isset( $variant['code'] ) ? (string) $variant['code'] : '', | |
| 84 | + 'file_name' => isset( $variant['file_name'] ) ? (string) $variant['file_name'] : '' | |
| 85 | + ]; | |
| 86 | + }, | |
| 87 | + $variants | |
| 88 | + ) | |
| 89 | + ) | |
| 90 | +); | |
| 91 | + | |
| 92 | +// Everything below indexes $variants[0]; keep the single-entry fallback so an | |
| 93 | +// empty or fully-invalid list still renders rather than fatalling. | |
| 94 | +if ( empty( $variants ) ) { | |
| 95 | + $variants = [ | |
| 96 | + [ | |
| 97 | + 'language' => $language, | |
| 98 | + 'code' => (string) $code_content, | |
| 99 | + 'file_name' => $file_name | |
| 100 | + ] | |
| 101 | + ]; | |
| 52 | 102 | } |
| 53 | 103 | |
| 104 | +$is_multi = count( $variants ) > 1; | |
| 105 | +$active = $variants[0]; | |
| 106 | +$active_lang = sanitize_text_field( $active['language'] ); | |
| 107 | + | |
| 108 | +// Dropdown/tab label for an entry: a custom filename overrides (so the API-docs | |
| 109 | +// cURL sample reads "cURL", not "Bash"), otherwise the language's display name. | |
| 110 | +// The block default filename ("filename.js") never wins. | |
| 111 | +$lang_label = function ( $variant ) { | |
| 112 | + $file = isset( $variant['file_name'] ) ? (string) $variant['file_name'] : ''; | |
| 113 | + if ( '' !== $file && 'filename.js' !== $file ) { | |
| 114 | + return $file; | |
| 115 | + } | |
| 116 | + return Helper::get_language_label( sanitize_text_field( isset( $variant['language'] ) ? $variant['language'] : '' ) ); | |
| 117 | +}; | |
| 118 | + | |
| 54 | 119 | // Enqueue necessary assets |
| 55 | 120 | wp_enqueue_script( 'betterdocs-code-snippet' ); |
| 56 | 121 | wp_enqueue_style( 'betterdocs-code-snippet' ); |
| 57 | 122 | ?> |
| 58 | 123 | |
| 59 | -<div class="betterdocs-code-snippet-wrapper theme-<?php echo esc_attr( $theme ); ?> <?php echo esc_attr( $snippet_id ); ?>" | |
| 124 | +<div class="betterdocs-code-snippet-wrapper theme-<?php echo esc_attr( $theme ); ?> <?php echo esc_attr( $snippet_id ); ?><?php echo $is_multi ? ' is-multi-lang' : ''; ?>" | |
| 60 | 125 | id="<?php echo esc_attr( $snippet_id ); ?>" |
| 61 | - data-language="<?php echo esc_attr( $language ); ?>" | |
| 126 | + data-language="<?php echo esc_attr( $active_lang ); ?>" | |
| 127 | + <?php echo $is_multi ? 'data-multi-lang="true"' : ''; ?> | |
| 62 | 128 | data-copy-button="<?php echo $show_copy_button ? 'true' : 'false'; ?>"> |
| 63 | 129 | |
| 64 | 130 | <?php if ( $show_header ) : ?> |
| 65 | 131 | <div class="betterdocs-code-snippet-header betterdocs-file-preview-header"> |
| @@ -71,57 +137,88 @@ | ||
| 71 | 137 | <span class="traffic-light traffic-light-green"></span> |
| 72 | 138 | </div> |
| 73 | 139 | <?php endif; ?> |
| 74 | 140 | |
| 75 | - <div class="betterdocs-file-info"> | |
| 76 | - <?php if ( $show_file_icon ) : ?> | |
| 77 | - <div class="betterdocs-file-icon"> | |
| 78 | - <?php if ( ! empty( $file_icon ) ) : ?> | |
| 79 | - <img src="<?php echo esc_url( $file_icon ); ?>" alt="<?php esc_attr_e( 'File icon', 'betterdocs' ); ?>" /> | |
| 80 | - <?php else : ?> | |
| 81 | - <span class="betterdocs-file-icon-emoji"><?php echo esc_html( Helper::get_file_icon_by_language( $language ) ); ?></span> | |
| 82 | - <?php endif; ?> | |
| 83 | - </div> | |
| 84 | - <?php endif; ?> | |
| 141 | + <?php if ( ! $is_multi ) : ?> | |
| 142 | + <div class="betterdocs-file-info"> | |
| 143 | + <?php if ( $show_file_icon ) : ?> | |
| 144 | + <div class="betterdocs-file-icon"> | |
| 145 | + <?php if ( ! empty( $file_icon ) ) : ?> | |
| 146 | + <img src="<?php echo esc_url( $file_icon ); ?>" alt="<?php esc_attr_e( 'File icon', 'betterdocs' ); ?>" /> | |
| 147 | + <?php else : ?> | |
| 148 | + <span class="betterdocs-file-icon-emoji"><?php echo esc_html( Helper::get_file_icon_by_language( $active_lang ) ); ?></span> | |
| 149 | + <?php endif; ?> | |
| 150 | + </div> | |
| 151 | + <?php endif; ?> | |
| 85 | 152 | |
| 86 | - <?php if ( ! empty( $file_name ) ) : ?> | |
| 87 | - <div class="betterdocs-file-name"> | |
| 88 | - <span class="file-name-text"><?php echo esc_html( $file_name ); ?></span> | |
| 89 | - </div> | |
| 90 | - <?php endif; ?> | |
| 91 | - </div> | |
| 153 | + <?php if ( ! empty( $file_name ) ) : ?> | |
| 154 | + <div class="betterdocs-file-name"> | |
| 155 | + <span class="file-name-text"><?php echo esc_html( $file_name ); ?></span> | |
| 156 | + </div> | |
| 157 | + <?php endif; ?> | |
| 158 | + </div> | |
| 159 | + <?php endif; ?> | |
| 92 | 160 | </div> |
| 93 | 161 | |
| 94 | 162 | <div class="betterdocs-file-preview-right"> |
| 95 | - <?php if ( $show_copy_button ) : ?> | |
| 96 | - <div class="betterdocs-code-snippet-copy-container"> | |
| 97 | - <button class="betterdocs-code-snippet-copy-button" | |
| 98 | - type="button" | |
| 99 | - data-clipboard-target="#<?php echo esc_attr( $snippet_id ); ?> .betterdocs-code-snippet-code code" | |
| 100 | - aria-label="<?php esc_attr_e( 'Copy code to clipboard', 'betterdocs' ); ?>"> | |
| 101 | - <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
| 102 | - <path d="M16 1H4C2.9 1 2 1.9 2 3V17H4V3H16V1ZM19 5H8C6.9 5 6 5.9 6 7V21C6 22.1 6.9 23 8 23H19C20.1 23 21 22.1 21 21V7C21 5.9 20.1 5 19 5ZM19 21H8V7H19V21Z" fill="currentColor"/> | |
| 163 | + <?php if ( $is_multi ) : ?> | |
| 164 | + <div class="betterdocs-code-lang-dropdown"> | |
| 165 | + <button type="button" class="betterdocs-code-lang-toggle" aria-haspopup="listbox" aria-expanded="false"> | |
| 166 | + <span class="betterdocs-code-lang-current"> | |
| 167 | + <?php if ( $show_file_icon ) : ?> | |
| 168 | + <span class="betterdocs-file-icon-emoji"><?php echo esc_html( Helper::get_file_icon_by_language( $active_lang ) ); ?></span> | |
| 169 | + <?php endif; ?> | |
| 170 | + <?php if ( $show_language_label ) : ?> | |
| 171 | + <span class="betterdocs-code-lang-label"><?php echo esc_html( $lang_label( $active ) ); ?></span> | |
| 172 | + <?php endif; ?> | |
| 173 | + </span> | |
| 174 | + <svg class="betterdocs-code-lang-caret" width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
| 175 | + <path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |
| 103 | 176 | </svg> |
| 104 | 177 | </button> |
| 105 | - <?php if ( $show_copy_tooltip ) : ?> | |
| 106 | - <div class="betterdocs-code-snippet-tooltip"><?php esc_html_e( 'Copy to clipboard', 'betterdocs' ); ?></div> | |
| 107 | - <?php endif; ?> | |
| 178 | + <ul class="betterdocs-code-lang-menu" role="listbox"> | |
| 179 | + <?php foreach ( $variants as $i => $variant ) : ?> | |
| 180 | + <?php $v_lang = sanitize_text_field( $variant['language'] ); ?> | |
| 181 | + <li class="betterdocs-code-lang-option<?php echo 0 === $i ? ' is-active' : ''; ?>" | |
| 182 | + role="option" | |
| 183 | + data-lang-index="<?php echo esc_attr( $i ); ?>"> | |
| 184 | + <?php if ( $show_file_icon ) : ?> | |
| 185 | + <span class="betterdocs-file-icon-emoji"><?php echo esc_html( Helper::get_file_icon_by_language( $v_lang ) ); ?></span> | |
| 186 | + <?php endif; ?> | |
| 187 | + <span class="betterdocs-code-lang-label"><?php echo esc_html( $lang_label( $variant ) ); ?></span> | |
| 188 | + </li> | |
| 189 | + <?php endforeach; ?> | |
| 190 | + </ul> | |
| 108 | 191 | </div> |
| 109 | 192 | <?php endif; ?> |
| 193 | + | |
| 194 | + <?php if ( $show_copy_button ) : ?> | |
| 195 | + <?php Helper::code_snippet_copy_button(); ?> | |
| 196 | + <?php endif; ?> | |
| 110 | 197 | </div> |
| 111 | 198 | </div> |
| 112 | 199 | <?php endif; ?> |
| 113 | 200 | |
| 114 | 201 | <div class="betterdocs-code-snippet-content"> |
| 115 | - <?php if ( $show_line_numbers ) : ?> | |
| 116 | - <div class="betterdocs-code-snippet-line-numbers" aria-hidden="true"> | |
| 117 | - <?php foreach ( $line_numbers as $line_num ) : ?> | |
| 118 | - <div class="line-number"><?php echo esc_html( $line_num ); ?></div> | |
| 119 | - <?php endforeach; ?> | |
| 202 | + <?php foreach ( $variants as $i => $variant ) : ?> | |
| 203 | + <?php | |
| 204 | + $v_lang = sanitize_text_field( $variant['language'] ); | |
| 205 | + $v_code = (string) $variant['code']; | |
| 206 | + ?> | |
| 207 | + <div class="betterdocs-code-snippet-panel<?php echo 0 === $i ? ' is-active' : ''; ?>" | |
| 208 | + data-lang-index="<?php echo esc_attr( $i ); ?>" | |
| 209 | + <?php echo 0 === $i ? '' : 'hidden'; ?>> | |
| 210 | + <?php if ( $show_line_numbers ) : ?> | |
| 211 | + <div class="betterdocs-code-snippet-line-numbers" aria-hidden="true"> | |
| 212 | + <?php foreach ( range( 1, max( 1, substr_count( $v_code, "\n" ) + 1 ) ) as $line_num ) : ?> | |
| 213 | + <div class="line-number"><?php echo esc_html( $line_num ); ?></div> | |
| 214 | + <?php endforeach; ?> | |
| 215 | + </div> | |
| 216 | + <?php endif; ?> | |
| 217 | + | |
| 218 | + <pre class="betterdocs-code-snippet-code language-<?php echo esc_attr( $v_lang ); ?>"><code><?php echo esc_html( $v_code ); ?></code></pre> | |
| 120 | 219 | </div> |
| 121 | - <?php endif; ?> | |
| 122 | - | |
| 123 | - <pre class="betterdocs-code-snippet-code language-<?php echo esc_attr( $language ); ?>"><code><?php echo esc_html( $code_content ); ?></code></pre> | |
| 220 | + <?php endforeach; ?> | |
| 124 | 221 | </div> |
| 125 | 222 | </div> |
| 126 | 223 | |
| 127 | 224 | <?php |