| 1 |
<?php |
| 2 |
/** |
| 3 |
* Global Injector Inline CSS |
| 4 |
* |
| 5 |
* Single source for Global Injector frontend CSS. Output is built from chunks |
| 6 |
* (base, button, icon, cover, shared, positions, print) and minified at output time. |
| 7 |
* |
| 8 |
* @package CTC |
| 9 |
* @since 5.0.2 |
| 10 |
*/ |
| 11 |
|
| 12 |
namespace CTC\Global_Injector; |
| 13 |
|
| 14 |
use CTC\Global_Injector\Styles\Button; |
| 15 |
use CTC\Global_Injector\Styles\Cover; |
| 16 |
use CTC\Global_Injector\Styles\Icon; |
| 17 |
|
| 18 |
/** |
| 19 |
* Inline_CSS |
| 20 |
* |
| 21 |
* Assembles Global Injector frontend CSS from style classes (Button, Icon, Cover) |
| 22 |
* and layout chunks (base, shared, positions, print). Source is unminified; minification at output. |
| 23 |
* |
| 24 |
* @since 5.0.2 |
| 25 |
*/ |
| 26 |
class Inline_CSS { |
| 27 |
|
| 28 |
/** |
| 29 |
* Build full CSS string for the given needed styles and positions (unminified). |
| 30 |
* |
| 31 |
* @since 5.0.2 |
| 32 |
* @param array $needed_styles Optional. List of 'button', 'icon', 'cover'. Default all. |
| 33 |
* @param array $needed_positions Optional. List of position keys. Default all inside + outside if any outside. |
| 34 |
* @return string Unminified CSS. |
| 35 |
*/ |
| 36 |
public static function build( array $needed_styles = [], array $needed_positions = [] ) { |
| 37 |
$chunks = self::get_chunks(); |
| 38 |
$use_styles = ! empty( $needed_styles ) ? $needed_styles : [ 'button', 'icon', 'cover' ]; |
| 39 |
$use_positions = ! empty( $needed_positions ) ? $needed_positions : array_keys( $chunks['positions'] ); |
| 40 |
|
| 41 |
$css = $chunks['base']; |
| 42 |
|
| 43 |
if ( in_array( 'button', $use_styles, true ) ) { |
| 44 |
$css .= $chunks['button']; |
| 45 |
} |
| 46 |
if ( in_array( 'icon', $use_styles, true ) ) { |
| 47 |
$css .= $chunks['icon']; |
| 48 |
} |
| 49 |
if ( in_array( 'cover', $use_styles, true ) ) { |
| 50 |
$css .= $chunks['cover']; |
| 51 |
} |
| 52 |
if ( in_array( 'button', $use_styles, true ) || in_array( 'icon', $use_styles, true ) ) { |
| 53 |
$css .= $chunks['shared']; |
| 54 |
} |
| 55 |
|
| 56 |
$has_outside = false; |
| 57 |
foreach ( $use_positions as $p ) { |
| 58 |
if ( strpos( (string) $p, 'outside' ) === 0 ) { |
| 59 |
$has_outside = true; |
| 60 |
break; |
| 61 |
} |
| 62 |
} |
| 63 |
if ( $has_outside && isset( $chunks['positions']['outside'] ) ) { |
| 64 |
$css .= $chunks['positions']['outside']; |
| 65 |
} |
| 66 |
foreach ( $use_positions as $pos ) { |
| 67 |
if ( isset( $chunks['positions'][ $pos ] ) ) { |
| 68 |
$css .= $chunks['positions'][ $pos ]; |
| 69 |
} |
| 70 |
} |
| 71 |
|
| 72 |
$css .= $chunks['print']; |
| 73 |
return $css; |
| 74 |
} |
| 75 |
|
| 76 |
/** |
| 77 |
* Get raw CSS chunks (unminified). Keys: base, button, icon, cover, shared, positions, print. |
| 78 |
* Button, icon, cover come from style classes; base, shared, positions, print are layout/structure. |
| 79 |
* |
| 80 |
* @since 5.0.2 |
| 81 |
* @return array<string, string|array<string, string>> |
| 82 |
*/ |
| 83 |
public static function get_chunks() { |
| 84 |
$positions = self::get_positions_chunks(); |
| 85 |
return [ |
| 86 |
'base' => self::get_base(), |
| 87 |
'button' => Button::get_global_injector_css(), |
| 88 |
'icon' => Icon::get_global_injector_css(), |
| 89 |
'cover' => Cover::get_global_injector_css(), |
| 90 |
'shared' => self::get_shared(), |
| 91 |
'positions' => $positions, |
| 92 |
'print' => self::get_print(), |
| 93 |
]; |
| 94 |
} |
| 95 |
|
| 96 |
/** |
| 97 |
* Base layout and copy button reset (unminified). |
| 98 |
* |
| 99 |
* @return string |
| 100 |
*/ |
| 101 |
private static function get_base() { |
| 102 |
return '/* CTC Global Injector */ |
| 103 |
.ctc-wrapper { |
| 104 |
position: relative; |
| 105 |
} |
| 106 |
.ctc-copy-btn { |
| 107 |
display: inline-flex; |
| 108 |
align-items: center; |
| 109 |
gap: 6px; |
| 110 |
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; |
| 111 |
font-weight: var(--ctc-font-weight, 500); |
| 112 |
line-height: 1.4; |
| 113 |
cursor: pointer; |
| 114 |
border: none; |
| 115 |
outline: none; |
| 116 |
transition: all 0.15s ease; |
| 117 |
} |
| 118 |
.ctc-copy-btn:focus { |
| 119 |
outline: 2px solid var(--ctc-bg, #4f46e5); |
| 120 |
outline-offset: 2px; |
| 121 |
} |
| 122 |
.ctc-copy-btn:active { |
| 123 |
transform: scale(0.97); |
| 124 |
} |
| 125 |
'; |
| 126 |
} |
| 127 |
|
| 128 |
/** |
| 129 |
* Shared (button + icon) states and icon/copied (unminified). |
| 130 |
* |
| 131 |
* @return string |
| 132 |
*/ |
| 133 |
private static function get_shared() { |
| 134 |
return ' |
| 135 |
.ctc-copy-btn__icon { |
| 136 |
display: inline-flex; |
| 137 |
align-items: center; |
| 138 |
justify-content: center; |
| 139 |
flex-shrink: 0; |
| 140 |
} |
| 141 |
.ctc-copy-btn__icon svg { |
| 142 |
display: block; |
| 143 |
fill: transparent; |
| 144 |
width: 14px; |
| 145 |
height: 14px; |
| 146 |
} |
| 147 |
.ctc-copy-btn--copied svg { |
| 148 |
fill: currentColor; |
| 149 |
} |
| 150 |
.ctc-copy-btn__text { |
| 151 |
white-space: nowrap; |
| 152 |
} |
| 153 |
.ctc-copy-btn--copied { |
| 154 |
animation: ctc-pulse 0.3s ease; |
| 155 |
} |
| 156 |
.ctc-copy-btn--error { |
| 157 |
opacity: 0.7; |
| 158 |
} |
| 159 |
@keyframes ctc-pulse { |
| 160 |
0%, 100% { transform: scale(1); } |
| 161 |
50% { transform: scale(1.05); } |
| 162 |
} |
| 163 |
'; |
| 164 |
} |
| 165 |
|
| 166 |
/** |
| 167 |
* Position chunks: inside_* and outside (unminified). |
| 168 |
* |
| 169 |
* @return array<string, string> |
| 170 |
*/ |
| 171 |
private static function get_positions_chunks() { |
| 172 |
$all_inside = [ 'inside_top_left', 'inside_top_right', 'inside_bottom_left', 'inside_bottom_right' ]; |
| 173 |
$positions = []; |
| 174 |
foreach ( $all_inside as $p ) { |
| 175 |
$sel = '.ctc-wrapper--' . $p . ' .ctc-copy-btn--button,.ctc-wrapper--' . $p . ' .ctc-copy-btn--icon'; |
| 176 |
$parts = explode( '_', str_replace( 'inside_', '', $p ), 2 ); |
| 177 |
$vert = $parts[0] ?? 'top'; |
| 178 |
$horz = $parts[1] ?? 'right'; |
| 179 |
$top = ( $vert === 'top' ) ? '8px' : 'auto'; |
| 180 |
$bottom = ( $vert === 'bottom' ) ? '8px' : 'auto'; |
| 181 |
$left = ( $horz === 'left' ) ? '8px' : 'auto'; |
| 182 |
$right = ( $horz === 'right' ) ? '8px' : 'auto'; |
| 183 |
$positions[ $p ] = $sel . ' { |
| 184 |
position: absolute; |
| 185 |
top: ' . $top . '; |
| 186 |
right: ' . $right . '; |
| 187 |
bottom: ' . $bottom . '; |
| 188 |
left: ' . $left . '; |
| 189 |
} |
| 190 |
'; |
| 191 |
} |
| 192 |
$positions['outside'] = ' |
| 193 |
.ctc-outside { |
| 194 |
display: flex; |
| 195 |
margin: 4px 0; |
| 196 |
} |
| 197 |
.ctc-outside--outside_before_left, |
| 198 |
.ctc-outside--outside_after_left { |
| 199 |
justify-content: flex-start; |
| 200 |
} |
| 201 |
.ctc-outside--outside_before_right, |
| 202 |
.ctc-outside--outside_after_right { |
| 203 |
justify-content: flex-end; |
| 204 |
} |
| 205 |
.ctc-wrapper--outside_before_left .ctc-copy-btn, |
| 206 |
.ctc-wrapper--outside_before_right .ctc-copy-btn, |
| 207 |
.ctc-wrapper--outside_after_left .ctc-copy-btn, |
| 208 |
.ctc-wrapper--outside_after_right .ctc-copy-btn { |
| 209 |
position: relative; |
| 210 |
top: auto; |
| 211 |
right: auto; |
| 212 |
bottom: auto; |
| 213 |
left: auto; |
| 214 |
} |
| 215 |
'; |
| 216 |
return $positions; |
| 217 |
} |
| 218 |
|
| 219 |
/** |
| 220 |
* Print hide (unminified). |
| 221 |
* |
| 222 |
* @return string |
| 223 |
*/ |
| 224 |
private static function get_print() { |
| 225 |
return ' |
| 226 |
@media print { |
| 227 |
.ctc-copy-btn, |
| 228 |
.ctc-cover-overlay { |
| 229 |
display: none !important; |
| 230 |
} |
| 231 |
} |
| 232 |
'; |
| 233 |
} |
| 234 |
} |
| 235 |
|