Changeset 3416780
- Timestamp:
- 12/10/2025 08:49:08 PM (4 months ago)
- Location:
- pixel-clusters/trunk
- Files:
-
- 6 added
- 5 edited
-
blocks (added)
-
blocks/cluster-block-editor.css (added)
-
blocks/cluster-block.js (added)
-
blocks/cluster-block.php (added)
-
content/pixel-html.php (modified) (1 diff)
-
css/admin.css (added)
-
css/pixel-clusters.css (modified) (1 diff)
-
js/admin.js (added)
-
js/cluster.js (modified) (3 diffs)
-
pixel-clusters.php (modified) (4 diffs)
-
readme.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
pixel-clusters/trunk/content/pixel-html.php
r2175363 r3416780 1 <style> 2 .wrap .postbox .inside h1 3 { 4 font-size:28px; 1 <?php 2 /** 3 * Pixel Clusters - Admin Page Template 4 * Version: 2.0.0 5 * Modern AJAX-based admin interface 6 */ 7 8 if ( ! defined( 'ABSPATH' ) ) { 9 exit; 5 10 } 6 11 7 .wrap .postbox .inside h3#code-generate 8 { 9 display: inline-block; 10 font-size: 18px; 11 } 12 13 .wrap .postbox .inside .mybutton 14 { 15 background: #09F; 16 border: 1px solid black; 17 border-radius: 10px; 18 color: white; 19 cursor: pointer; 20 font-size: 18px; 21 font-weight: 700; 22 padding: 4px 15px; 23 } 24 25 .wrap .postbox .inside .mybutton:hover 26 { 27 background: black; 28 } 29 30 .wrap .postbox .inside #success 31 { 32 color: #0C3; 33 font-style: 16px; 34 font-style: italic; 35 } 36 37 </style> 38 <div class="wrap"> 12 $types = pixel_cluster_post_type(); 13 $has_woocommerce = class_exists( 'WooCommerce' ); 14 ?> 15 16 <div class="pixel-clusters-wrap"> 39 17 40 <div class="postbox"> 41 <div class="inside"> 42 43 <h1>Pixel Clusters</h1> 44 <hr /> 45 <h2>Copy Code:</h2> 46 <h3 id="code-generate">[cluster]</h3> <button class="mybutton" onclick="pixel_copyToClipboard('#code-generate')">Copy</button> <span id="success"></span> 47 48 <table width="100%" border="0" cellpadding="1" cellspacing="1"> 49 <tr> 50 <td width="15%"> 51 <label for="mode">Select mode:</label> 52 </td> 53 <td> 54 <select id="mode" name="mode" onChange="pixel_cluster_code()"> 55 <option disabled selected hidden>Select mode:</option> 56 <option value="1">Image, title and excerpt</option> 57 <option value="2">Image, title without excerpt</option> 58 <option value="3">List of titles</option> 59 </select> 60 </td> 61 </tr> 62 <tr> 63 <td width="15%"> 64 <label for="number">Number of post to show:</label> 65 </td> 66 <td> 67 <input type="number" min="1" id="number" name="number" value="1" onChange="pixel_cluster_code()" /> 68 </td> 69 </tr> 70 <tr> 71 <td width="15%"> 72 <label for="type">Select type:</label> 73 </td> 74 <td> 75 <select id="type" name="type" onChange="pixel_cluster_change_type()"> 76 <option disabled selected hidden>Select type:</option> 77 <option value="1">Categories</option> 78 <option value="2">Tags</option> 79 </select> 80 </td> 81 </tr> 82 <tr class="type-select" id="type1" style="display: none;"> 83 <td width="15%"> 84 <label for="cat">Select a category:</label> 85 </td> 86 <td> 87 <?php 88 wp_dropdown_categories( array( 89 'hide_empty' => true, 90 'name' => 'cat', 91 'id' => 'cat', 92 'hierarchical' => true, 93 'show_option_none' => 'Select category', 94 'orderby' => 'name', 95 ) ); 96 ?> 97 </td> 98 </tr> 99 <tr class="type-select" id="type2" style="display: none;"> 100 <td width="15%"> 101 <label for="tag">Select a tag:</label> 102 </td> 103 <td> 104 <?php 105 wp_dropdown_categories( array( 106 'hide_empty' => true, 107 'name' => 'tag', 108 'id' => 'tag', 109 'hierarchical' => true, 110 'show_option_none' => 'Select tag', 111 'taxonomy' => 'post_tag', 112 'orderby' => 'name', 113 ) ); 114 ?> 115 </td> 116 </tr> 117 </table> 118 119 <p> </p> 120 121 <p><strong>Example: [cluster type="1" tag_id="51" modo="1" numero="2"]</strong></p> 122 123 <h2>Type:</h2> 124 <ul> 125 <li>1 - Category</li> 126 <li>2 - Tag</li> 127 </ul> 128 129 <h2>tag_id:</h2> 130 <p>Reference ID</p> 131 132 133 <h2>modo:</h2> 134 <ul> 135 <li>1 - Image, title and excerpt</li> 136 <li>2 - Image, title without excerpt</li> 137 <li>3 - List of titles</li> 138 </ul> 139 140 141 <h2>numero:</h2> 142 <p>Number of posts to show</p> 143 144 </div> 145 </div> 18 <!-- Header --> 19 <header class="pc-header"> 20 <div class="pc-header-content"> 21 <h1> 22 <?php esc_html_e( 'Pixel Clusters', 'pixel-cluster' ); ?> 23 <span class="pc-version">v<?php echo esc_html( PIXEL_CLUSTER_VERSION ); ?></span> 24 </h1> 25 <p><?php esc_html_e( 'Create beautiful post clusters with shortcodes', 'pixel-cluster' ); ?></p> 26 </div> 27 <div class="pc-header-icon"> 28 <span class="dashicons dashicons-grid-view"></span> 29 </div> 30 </header> 31 32 <!-- Tabs Navigation --> 33 <div class="pc-tabs"> 34 <button type="button" class="pc-tab active" data-tab="pc-tab-generator"> 35 <span class="dashicons dashicons-shortcode"></span> 36 <?php esc_html_e( 'Generator', 'pixel-cluster' ); ?> 37 </button> 38 <button type="button" class="pc-tab" data-tab="pc-tab-help"> 39 <span class="dashicons dashicons-editor-help"></span> 40 <?php esc_html_e( 'Documentation', 'pixel-cluster' ); ?> 41 </button> 42 </div> 43 44 <!-- Tab: Generator --> 45 <div id="pc-tab-generator" class="pc-tab-content active"> 46 47 <!-- Generated Code Card --> 48 <div class="pc-card"> 49 <div class="pc-card-header"> 50 <span class="dashicons dashicons-shortcode"></span> 51 <h2><?php esc_html_e( 'Generated Shortcode', 'pixel-cluster' ); ?></h2> 52 </div> 53 <div class="pc-card-body"> 54 <div class="pc-code-display"> 55 <code id="pc-code-output" class="pc-code-output">[cluster]</code> 56 <button type="button" id="pc-copy-btn" class="pc-btn pc-btn-primary"> 57 <span class="dashicons dashicons-clipboard"></span> 58 <?php esc_html_e( 'Copy', 'pixel-cluster' ); ?> 59 </button> 60 <span id="pc-success-message" class="pc-success-message"> 61 <span class="dashicons dashicons-yes-alt"></span> 62 <?php esc_html_e( 'Copied!', 'pixel-cluster' ); ?> 63 </span> 64 </div> 65 </div> 66 </div> 67 68 <!-- Configuration Card --> 69 <div class="pc-card"> 70 <div class="pc-card-header"> 71 <span class="dashicons dashicons-admin-generic"></span> 72 <h2><?php esc_html_e( 'Configuration', 'pixel-cluster' ); ?></h2> 73 </div> 74 <div class="pc-card-body"> 75 <div class="pc-form-grid"> 76 77 <!-- Display Mode --> 78 <div class="pc-form-group"> 79 <label for="pc-mode"> 80 <span class="dashicons dashicons-visibility"></span> 81 <?php esc_html_e( 'Display Mode', 'pixel-cluster' ); ?> 82 </label> 83 <select id="pc-mode" name="mode" class="pc-select"> 84 <option value="1"><?php esc_html_e( 'Image, title and excerpt', 'pixel-cluster' ); ?></option> 85 <option value="2"><?php esc_html_e( 'Image and title (cards)', 'pixel-cluster' ); ?></option> 86 <option value="3"><?php esc_html_e( 'List of titles', 'pixel-cluster' ); ?></option> 87 <?php if ( $has_woocommerce ) : ?> 88 <option value="4"><?php esc_html_e( 'Products (WooCommerce)', 'pixel-cluster' ); ?></option> 89 <?php endif; ?> 90 </select> 91 </div> 92 93 <!-- Number of posts --> 94 <div class="pc-form-group"> 95 <label for="pc-number"> 96 <span class="dashicons dashicons-editor-ol"></span> 97 <?php esc_html_e( 'Number of posts', 'pixel-cluster' ); ?> 98 </label> 99 <input type="number" id="pc-number" name="number" class="pc-input" value="4" min="1" max="50" /> 100 </div> 101 102 <!-- Content Type --> 103 <div class="pc-form-group"> 104 <label for="pc-type"> 105 <span class="dashicons dashicons-category"></span> 106 <?php esc_html_e( 'Content Type', 'pixel-cluster' ); ?> 107 </label> 108 <select id="pc-type" name="type" class="pc-select"> 109 <option value="" disabled selected><?php esc_html_e( 'Select type...', 'pixel-cluster' ); ?></option> 110 <option value="1"><?php esc_html_e( 'Categories', 'pixel-cluster' ); ?></option> 111 <option value="2"><?php esc_html_e( 'Tags', 'pixel-cluster' ); ?></option> 112 <?php if ( ! empty( $types['name_type'] ) ) : ?> 113 <option value="3"><?php esc_html_e( 'Custom Post Type', 'pixel-cluster' ); ?></option> 114 <?php endif; ?> 115 <?php if ( $has_woocommerce ) : ?> 116 <option value="4"><?php esc_html_e( 'WooCommerce Categories', 'pixel-cluster' ); ?></option> 117 <option value="5"><?php esc_html_e( 'WooCommerce Tags', 'pixel-cluster' ); ?></option> 118 <?php endif; ?> 119 </select> 120 </div> 121 122 </div> 123 124 <!-- Dynamic Fields Container --> 125 <div class="pc-form-grid" style="margin-top: 20px;"> 126 127 <!-- Categories Field --> 128 <div id="pc-field-cat" class="pc-form-group pc-dynamic-field"> 129 <label for="pc-cat"> 130 <span class="dashicons dashicons-category"></span> 131 <?php esc_html_e( 'Select Category', 'pixel-cluster' ); ?> 132 </label> 133 <?php 134 wp_dropdown_categories( array( 135 'hide_empty' => true, 136 'name' => 'cat', 137 'id' => 'pc-cat', 138 'class' => 'pc-select', 139 'hierarchical' => true, 140 'show_option_none' => __( 'All categories', 'pixel-cluster' ), 141 'orderby' => 'name', 142 ) ); 143 ?> 144 </div> 145 146 <!-- Tags Field --> 147 <div id="pc-field-tag" class="pc-form-group pc-dynamic-field"> 148 <label for="pc-tag"> 149 <span class="dashicons dashicons-tag"></span> 150 <?php esc_html_e( 'Select Tag', 'pixel-cluster' ); ?> 151 </label> 152 <?php 153 wp_dropdown_categories( array( 154 'hide_empty' => true, 155 'name' => 'tag', 156 'id' => 'pc-tag', 157 'class' => 'pc-select', 158 'hierarchical' => true, 159 'show_option_none' => __( 'All tags', 'pixel-cluster' ), 160 'taxonomy' => 'post_tag', 161 'orderby' => 'name', 162 ) ); 163 ?> 164 </div> 165 166 <!-- Post Type Field --> 167 <div id="pc-field-post-type" class="pc-form-group pc-dynamic-field"> 168 <label for="pc-post-type"> 169 <span class="dashicons dashicons-admin-post"></span> 170 <?php esc_html_e( 'Select Post Type', 'pixel-cluster' ); ?> 171 </label> 172 <select name="post_type" id="pc-post-type" class="pc-select"> 173 <option value="-1"><?php esc_html_e( 'Select post type...', 'pixel-cluster' ); ?></option> 174 <?php foreach ( $types['name_type'] as $type ) : ?> 175 <option value="<?php echo esc_attr( $type ); ?>"><?php echo esc_html( $type ); ?></option> 176 <?php endforeach; ?> 177 </select> 178 </div> 179 180 <!-- WooCommerce Categories --> 181 <?php if ( $has_woocommerce ) : ?> 182 <div id="pc-field-product-cat" class="pc-form-group pc-dynamic-field"> 183 <label for="pc-product-cat"> 184 <span class="dashicons dashicons-cart"></span> 185 <?php esc_html_e( 'Product Category', 'pixel-cluster' ); ?> 186 </label> 187 <?php 188 wp_dropdown_categories( array( 189 'hide_empty' => true, 190 'name' => 'product_cat', 191 'id' => 'pc-product-cat', 192 'class' => 'pc-select', 193 'hierarchical' => true, 194 'show_option_none' => __( 'All categories', 'pixel-cluster' ), 195 'taxonomy' => 'product_cat', 196 'orderby' => 'name', 197 ) ); 198 ?> 199 </div> 200 201 <!-- WooCommerce Tags --> 202 <div id="pc-field-product-tag" class="pc-form-group pc-dynamic-field"> 203 <label for="pc-product-tag"> 204 <span class="dashicons dashicons-tag"></span> 205 <?php esc_html_e( 'Product Tag', 'pixel-cluster' ); ?> 206 </label> 207 <?php 208 wp_dropdown_categories( array( 209 'hide_empty' => true, 210 'name' => 'product_tag', 211 'id' => 'pc-product-tag', 212 'class' => 'pc-select', 213 'hierarchical' => true, 214 'show_option_none' => __( 'All tags', 'pixel-cluster' ), 215 'taxonomy' => 'product_tag', 216 'orderby' => 'name', 217 ) ); 218 ?> 219 </div> 220 <?php endif; ?> 221 222 </div> 223 224 <!-- Custom Post Type Taxonomies --> 225 <?php foreach ( $types['name_type'] as $type ) : ?> 226 <?php if ( ! empty( $types['name_taxonomies'][ $type ] ) ) : ?> 227 228 <div id="pc-field-taxonomy-<?php echo esc_attr( $type ); ?>" class="pc-form-group pc-dynamic-field pc-field-taxonomy" style="margin-top: 20px;"> 229 <label for="pc-taxonomy-<?php echo esc_attr( $type ); ?>"> 230 <span class="dashicons dashicons-portfolio"></span> 231 <?php esc_html_e( 'Select Taxonomy', 'pixel-cluster' ); ?> 232 </label> 233 <select name="taxonomy_<?php echo esc_attr( $type ); ?>" id="pc-taxonomy-<?php echo esc_attr( $type ); ?>" class="pc-select pc-taxonomy-select"> 234 <option value="-1"><?php esc_html_e( 'Select taxonomy...', 'pixel-cluster' ); ?></option> 235 <?php foreach ( $types['name_taxonomies'][ $type ] as $taxonomy ) : ?> 236 <option value="<?php echo esc_attr( $taxonomy ); ?>"><?php echo esc_html( $taxonomy ); ?></option> 237 <?php endforeach; ?> 238 </select> 239 </div> 240 241 <?php foreach ( $types['name_taxonomies'][ $type ] as $taxonomy ) : ?> 242 <div id="pc-field-term-<?php echo esc_attr( $taxonomy ); ?>" class="pc-form-group pc-dynamic-field pc-field-term" style="margin-top: 20px;"> 243 <label for="pc-term-<?php echo esc_attr( $taxonomy ); ?>"> 244 <span class="dashicons dashicons-list-view"></span> 245 <?php esc_html_e( 'Select Term', 'pixel-cluster' ); ?> 246 </label> 247 <?php 248 wp_dropdown_categories( array( 249 'hide_empty' => true, 250 'name' => 'term_' . $taxonomy, 251 'id' => 'pc-term-' . $taxonomy, 252 'class' => 'pc-select pc-term-select', 253 'hierarchical' => true, 254 'show_option_none' => __( 'All terms', 'pixel-cluster' ), 255 'taxonomy' => $taxonomy, 256 'orderby' => 'name', 257 ) ); 258 ?> 259 </div> 260 <?php endforeach; ?> 261 262 <?php endif; ?> 263 <?php endforeach; ?> 264 265 <!-- Preview Section --> 266 <div class="pc-preview-section"> 267 <div class="pc-preview-header"> 268 <h3> 269 <span class="dashicons dashicons-visibility"></span> 270 <?php esc_html_e( 'Preview', 'pixel-cluster' ); ?> 271 </h3> 272 <button type="button" id="pc-preview-btn" class="pc-btn pc-btn-secondary"> 273 <span class="dashicons dashicons-update"></span> 274 <?php esc_html_e( 'Load Preview', 'pixel-cluster' ); ?> 275 </button> 276 </div> 277 <div id="pc-preview-container" class="pc-preview-container"> 278 <div class="pc-preview-placeholder"> 279 <span class="dashicons dashicons-admin-appearance"></span> 280 <p><?php esc_html_e( 'Configure your shortcode and click "Load Preview" to see the result', 'pixel-cluster' ); ?></p> 281 </div> 282 </div> 283 </div> 284 285 </div> 286 </div> 287 288 </div> 289 290 <!-- Tab: Help --> 291 <div id="pc-tab-help" class="pc-tab-content"> 292 293 <div class="pc-card"> 294 <div class="pc-card-header"> 295 <span class="dashicons dashicons-book"></span> 296 <h2><?php esc_html_e( 'How to Use', 'pixel-cluster' ); ?></h2> 297 </div> 298 <div class="pc-card-body"> 299 300 <div class="pc-example-box"> 301 <h4> 302 <span class="dashicons dashicons-info"></span> 303 <?php esc_html_e( 'Example Shortcode', 'pixel-cluster' ); ?> 304 </h4> 305 <code>[cluster type="1" tag_id="5" modo="1" numero="4"]</code> 306 </div> 307 308 <div class="pc-help-grid" style="margin-top: 24px;"> 309 310 <div class="pc-help-item"> 311 <h4>type</h4> 312 <p><?php esc_html_e( 'Content source type', 'pixel-cluster' ); ?></p> 313 <ul> 314 <li><strong>1</strong> - <?php esc_html_e( 'Categories', 'pixel-cluster' ); ?></li> 315 <li><strong>2</strong> - <?php esc_html_e( 'Tags', 'pixel-cluster' ); ?></li> 316 <li><strong>3</strong> - <?php esc_html_e( 'Custom Post Type', 'pixel-cluster' ); ?></li> 317 <li><strong>4</strong> - <?php esc_html_e( 'WooCommerce Categories', 'pixel-cluster' ); ?></li> 318 <li><strong>5</strong> - <?php esc_html_e( 'WooCommerce Tags', 'pixel-cluster' ); ?></li> 319 </ul> 320 </div> 321 322 <div class="pc-help-item"> 323 <h4>tag_id</h4> 324 <p><?php esc_html_e( 'The ID of the category, tag, or term to filter by. Leave empty to show all.', 'pixel-cluster' ); ?></p> 325 </div> 326 327 <div class="pc-help-item"> 328 <h4>modo</h4> 329 <p><?php esc_html_e( 'Display mode for the cluster', 'pixel-cluster' ); ?></p> 330 <ul> 331 <li><strong>1</strong> - <?php esc_html_e( 'Image, title and excerpt', 'pixel-cluster' ); ?></li> 332 <li><strong>2</strong> - <?php esc_html_e( 'Image and title (cards)', 'pixel-cluster' ); ?></li> 333 <li><strong>3</strong> - <?php esc_html_e( 'List of titles only', 'pixel-cluster' ); ?></li> 334 <li><strong>4</strong> - <?php esc_html_e( 'Products with price and cart button', 'pixel-cluster' ); ?></li> 335 </ul> 336 </div> 337 338 <div class="pc-help-item"> 339 <h4>numero</h4> 340 <p><?php esc_html_e( 'Number of posts to display. Default is 4.', 'pixel-cluster' ); ?></p> 341 </div> 342 343 <div class="pc-help-item"> 344 <h4>post_type</h4> 345 <p><?php esc_html_e( 'For custom post types (type="3"), specify the post type slug.', 'pixel-cluster' ); ?></p> 346 </div> 347 348 <div class="pc-help-item"> 349 <h4>taxonomy</h4> 350 <p><?php esc_html_e( 'For custom post types, specify the taxonomy to filter by.', 'pixel-cluster' ); ?></p> 351 </div> 352 353 </div> 354 355 </div> 356 </div> 357 358 <!-- Quick Start Guide --> 359 <div class="pc-card"> 360 <div class="pc-card-header"> 361 <span class="dashicons dashicons-welcome-learn-more"></span> 362 <h2><?php esc_html_e( 'Quick Start Guide', 'pixel-cluster' ); ?></h2> 363 </div> 364 <div class="pc-card-body"> 365 <ol style="line-height: 2; padding-left: 20px;"> 366 <li><?php esc_html_e( 'Go to the Generator tab', 'pixel-cluster' ); ?></li> 367 <li><?php esc_html_e( 'Select a display mode (how the posts will look)', 'pixel-cluster' ); ?></li> 368 <li><?php esc_html_e( 'Choose the number of posts to show', 'pixel-cluster' ); ?></li> 369 <li><?php esc_html_e( 'Select the content type (categories, tags, etc.)', 'pixel-cluster' ); ?></li> 370 <li><?php esc_html_e( 'Optionally filter by a specific category or tag', 'pixel-cluster' ); ?></li> 371 <li><?php esc_html_e( 'Click "Copy" to copy the shortcode', 'pixel-cluster' ); ?></li> 372 <li><?php esc_html_e( 'Paste the shortcode in any post, page, or widget', 'pixel-cluster' ); ?></li> 373 </ol> 374 </div> 375 </div> 376 377 </div> 146 378 147 379 </div> 380 381 <!-- Toast Notification Container --> 382 <div id="pc-toast" class="pc-toast"></div> -
pixel-clusters/trunk/css/pixel-clusters.css
r2144690 r3416780 1 /* CSS Document */ 2 3 .cluster 4 { 5 display: inline-block; 6 padding: 10px 0 10px 0; 7 vertical-align: top; 8 width: 100%; 9 } 10 11 .cluster .category 12 { 13 border-bottom: 1px dashed #CCC; 14 display: inline-block; 15 margin-bottom: 30px; 16 padding-bottom: 10px; 17 vertical-align: top; 18 width: 100%; 19 } 20 21 .cluster .category .articles 22 { 23 float: left; 24 font-size: 1em; 25 font-weight: 400; 26 line-height: normal; 27 padding: 0 0 0 1%; 28 width: 79%; 29 } 30 31 .cluster .category .articles .h2 32 { 33 font-size: 1.2em; 34 font-weight: 700; 35 line-height: normal; 36 margin: 0 0 10px 0; 37 } 38 39 .cluster .category .images 40 { 41 float: left; 42 padding: 0 2% 0 0; 43 width: 18%; 44 } 45 46 .cluster .category .images img 47 { 48 height: auto; 49 max-width: 100%; 50 } 51 52 .cluster .column3 53 { 54 display: inline-block; 55 margin-bottom: 50px; 56 padding: 0 1.5%; 57 vertical-align: top; 58 width: 30%; 59 } 60 61 .cluster .column3 .articles 62 { 63 font-size: 0.8em; 64 font-weight: 400; 65 line-height: normal; 66 margin-top: 10px; 67 } 68 69 .cluster .column3 .articles .h2 a 70 { 1 /** 2 * Pixel Clusters - Frontend Styles 3 * Version: 2.0.0 4 * Modern responsive CSS for post clusters 5 */ 6 7 /* CSS Variables */ 8 :root { 9 --pc-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 10 --pc-primary-color: #2271b1; 11 --pc-text-color: #1d2327; 12 --pc-text-secondary: #50575e; 13 --pc-bg-light: #f6f7f7; 14 --pc-border-color: #dcdcde; 15 --pc-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); 16 --pc-shadow-hover: 0 4px 16px rgba(0, 0, 0, 0.12); 17 --pc-radius: 8px; 18 --pc-transition: all 0.3s ease; 19 --pc-btn-primary: #9bad3e; 20 --pc-btn-cart: #e96656; 21 --pc-btn-hover: #666666; 22 } 23 24 /* Base Cluster Container */ 25 .cluster, 26 .pc-cluster { 27 display: grid; 28 gap: 24px; 29 padding: 16px 0; 30 width: 100%; 31 } 32 33 /* Legacy support */ 34 .cluster { 35 display: inline-block; 36 padding: 10px 0; 37 vertical-align: top; 38 width: 100%; 39 } 40 41 /* Mode 1: Full articles with image, title, excerpt */ 42 .cluster .category, 43 .pc-cluster .pc-item--full { 44 display: grid; 45 grid-template-columns: 120px 1fr; 46 gap: 20px; 47 align-items: start; 48 padding: 20px; 49 background: var(--pc-bg-light); 50 border-radius: var(--pc-radius); 51 border: 1px solid var(--pc-border-color); 52 transition: var(--pc-transition); 53 } 54 55 .cluster .category:hover, 56 .pc-cluster .pc-item--full:hover { 57 box-shadow: var(--pc-shadow-hover); 58 transform: translateY(-2px); 59 } 60 61 /* Legacy support for category */ 62 .cluster .category { 63 border-bottom: 1px dashed #CCC; 64 display: inline-block; 65 margin-bottom: 30px; 66 padding-bottom: 10px; 67 vertical-align: top; 68 width: 100%; 69 } 70 71 .cluster .category .articles, 72 .pc-cluster .pc-item--full .pc-item__content { 71 73 font-size: 1em; 72 font-weight: 700; 73 line-height: normal; 74 } 75 76 .cluster .column3 .images 77 { 78 background-position: center center; 79 background-repeat: no-repeat; 80 background-size: cover; 81 height: 150px; 82 width: 100%; 83 } 74 font-weight: 400; 75 line-height: 1.6; 76 } 77 78 /* Legacy float support */ 79 .cluster .category .articles { 80 float: left; 81 padding: 0 0 0 1%; 82 width: 79%; 83 } 84 85 .cluster .category .articles .h2, 86 .pc-cluster .pc-item__title { 87 font-size: 1.15em; 88 font-weight: 600; 89 line-height: 1.4; 90 margin: 0 0 8px 0; 91 } 92 93 .cluster .category .articles .h2 a, 94 .pc-cluster .pc-item__title a { 95 color: var(--pc-text-color); 96 text-decoration: none; 97 transition: var(--pc-transition); 98 } 99 100 .cluster .category .articles .h2 a:hover, 101 .pc-cluster .pc-item__title a:hover { 102 color: var(--pc-primary-color); 103 } 104 105 .cluster .category .images, 106 .pc-cluster .pc-item--full .pc-item__image { 107 border-radius: var(--pc-radius); 108 overflow: hidden; 109 } 110 111 /* Legacy float support */ 112 .cluster .category .images { 113 float: left; 114 padding: 0 2% 0 0; 115 width: 18%; 116 } 117 118 .cluster .category .images img, 119 .pc-cluster .pc-item__img { 120 height: auto; 121 max-width: 100%; 122 border-radius: var(--pc-radius); 123 object-fit: cover; 124 transition: var(--pc-transition); 125 } 126 127 .cluster .category .images a:hover img, 128 .pc-cluster .pc-item--full a:hover .pc-item__img { 129 transform: scale(1.05); 130 } 131 132 .pc-cluster .pc-item__excerpt { 133 color: var(--pc-text-secondary); 134 font-size: 0.95em; 135 line-height: 1.6; 136 } 137 138 /* Mode 2: Cards with image and title */ 139 .cluster .column3, 140 .pc-cluster .pc-item--card { 141 display: inline-block; 142 width: calc(33.333% - 20px); 143 margin: 0 10px 24px; 144 vertical-align: top; 145 background: #fff; 146 border-radius: var(--pc-radius); 147 overflow: hidden; 148 box-shadow: var(--pc-shadow); 149 transition: var(--pc-transition); 150 } 151 152 .cluster .column3:hover, 153 .pc-cluster .pc-item--card:hover { 154 box-shadow: var(--pc-shadow-hover); 155 transform: translateY(-4px); 156 } 157 158 .cluster .column3 .articles, 159 .pc-cluster .pc-item--card .pc-item__content { 160 padding: 16px; 161 } 162 163 .cluster .column3 .articles .h2, 164 .pc-cluster .pc-item--card .pc-item__title { 165 margin: 0; 166 } 167 168 .cluster .column3 .articles .h2 a, 169 .pc-cluster .pc-item--card .pc-item__title a { 170 font-size: 0.95em; 171 font-weight: 600; 172 line-height: 1.4; 173 display: block; 174 color: var(--pc-text-color); 175 text-decoration: none; 176 } 177 178 .cluster .column3 .articles .h2 a:hover, 179 .pc-cluster .pc-item--card .pc-item__title a:hover { 180 color: var(--pc-primary-color); 181 } 182 183 .cluster .column3 .images, 184 .pc-cluster .pc-item--card .pc-item__image-bg, 185 .pc-cluster .pc-item--card .images { 186 background-position: center center; 187 background-repeat: no-repeat; 188 background-size: cover; 189 height: 180px; 190 width: 100%; 191 transition: var(--pc-transition); 192 } 193 194 .cluster .column3:hover .images, 195 .pc-cluster .pc-item--card:hover .pc-item__image-bg { 196 transform: scale(1.05); 197 } 198 199 .cluster .column3 .articles .price, 200 .pc-cluster .pc-item--card .pc-item__price { 201 margin: 10px 0; 202 font-weight: 600; 203 color: var(--pc-primary-color); 204 } 205 206 /* Mode 3: List style */ 207 .pc-cluster--mode-3 { 208 list-style: none; 209 padding: 0; 210 margin: 0; 211 } 212 213 .pc-cluster .pc-item--list, 214 .cluster li { 215 padding: 12px 16px; 216 border-bottom: 1px solid var(--pc-border-color); 217 transition: var(--pc-transition); 218 } 219 220 .pc-cluster .pc-item--list:last-child, 221 .cluster li:last-child { 222 border-bottom: none; 223 } 224 225 .pc-cluster .pc-item--list:hover, 226 .cluster li:hover { 227 background: var(--pc-bg-light); 228 padding-left: 24px; 229 } 230 231 .pc-cluster .pc-item--list a, 232 .cluster li a { 233 color: var(--pc-text-color); 234 text-decoration: none; 235 font-weight: 500; 236 transition: var(--pc-transition); 237 } 238 239 .pc-cluster .pc-item--list a:hover, 240 .cluster li a:hover { 241 color: var(--pc-primary-color); 242 } 243 244 /* Mode 4: Products (Flex layout) */ 245 .cluster.flex, 246 .pc-cluster--flex { 247 display: flex; 248 flex-wrap: wrap; 249 gap: 24px; 250 } 251 252 .cluster.flex .columns, 253 .pc-cluster .pc-item--product { 254 flex: 1 1 calc(33.333% - 24px); 255 min-width: 280px; 256 max-width: calc(33.333% - 16px); 257 background: #fff; 258 border-radius: var(--pc-radius); 259 overflow: hidden; 260 box-shadow: var(--pc-shadow); 261 transition: var(--pc-transition); 262 } 263 264 .cluster.flex .columns:hover, 265 .pc-cluster .pc-item--product:hover { 266 box-shadow: var(--pc-shadow-hover); 267 transform: translateY(-4px); 268 } 269 270 .cluster.flex .columns .articles, 271 .pc-cluster .pc-item--product .pc-item__content { 272 padding: 16px; 273 } 274 275 .cluster.flex .columns .articles .h2, 276 .pc-cluster .pc-item--product .pc-item__title { 277 margin: 0 0 8px 0; 278 } 279 280 .cluster.flex .columns .articles .h2 a, 281 .pc-cluster .pc-item--product .pc-item__title a { 282 font-size: 1em; 283 font-weight: 600; 284 line-height: 1.4; 285 color: var(--pc-text-color); 286 text-decoration: none; 287 } 288 289 .cluster.flex .columns .articles .h2 a:hover, 290 .pc-cluster .pc-item--product .pc-item__title a:hover { 291 color: var(--pc-primary-color); 292 } 293 294 .cluster.flex .columns .articles .price, 295 .pc-cluster .pc-item--product .pc-item__price { 296 margin: 12px 0; 297 font-size: 1.1em; 298 font-weight: 700; 299 color: var(--pc-primary-color); 300 } 301 302 .cluster.flex .columns .buttons, 303 .pc-cluster .pc-item--product .pc-item__buttons { 304 display: flex; 305 gap: 8px; 306 width: 100%; 307 } 308 309 .cluster.flex .columns .buttons a, 310 .cluster.flex .columns .buttons button, 311 .pc-cluster .pc-item--product .pc-btn { 312 flex: 1; 313 background: var(--pc-btn-primary); 314 border: none; 315 border-radius: var(--pc-radius); 316 color: white; 317 display: flex; 318 align-items: center; 319 justify-content: center; 320 font-weight: 600; 321 font-size: 0.85em; 322 padding: 12px 8px; 323 text-align: center; 324 text-transform: uppercase; 325 text-decoration: none; 326 cursor: pointer; 327 transition: var(--pc-transition); 328 } 329 330 .cluster.flex .columns .buttons a.carrito, 331 .cluster.flex .columns .buttons button.carrito, 332 .pc-cluster .pc-item--product .pc-btn--cart { 333 background: var(--pc-btn-cart); 334 } 335 336 .cluster.flex .columns .buttons a:hover, 337 .cluster.flex .columns .buttons button:hover, 338 .pc-cluster .pc-item--product .pc-btn:hover { 339 background: var(--pc-btn-hover); 340 transform: translateY(-2px); 341 } 342 343 .cluster.flex .columns .images, 344 .pc-cluster .pc-item--product .pc-item__image-bg, 345 .pc-cluster .pc-item--product .images { 346 background-position: center center; 347 background-repeat: no-repeat; 348 background-size: cover; 349 height: 200px; 350 width: 100%; 351 transition: var(--pc-transition); 352 } 353 354 .cluster.flex .columns:hover .images, 355 .pc-cluster .pc-item--product:hover .pc-item__image-bg { 356 transform: scale(1.05); 357 } 358 359 /* Image link wrapper */ 360 .pc-item__image-link { 361 display: block; 362 overflow: hidden; 363 } 364 365 /* Loading state for buttons */ 366 .pc-btn.loading { 367 position: relative; 368 color: transparent; 369 pointer-events: none; 370 } 371 372 .pc-btn.loading::after { 373 content: ''; 374 position: absolute; 375 width: 16px; 376 height: 16px; 377 border: 2px solid #fff; 378 border-top-color: transparent; 379 border-radius: 50%; 380 animation: spin 0.8s linear infinite; 381 } 382 383 @keyframes spin { 384 to { transform: rotate(360deg); } 385 } 386 387 /* Placeholder for missing images */ 388 .pc-item__placeholder { 389 display: block; 390 width: 100%; 391 height: 120px; 392 background: linear-gradient(135deg, #f0f0f0 0%, #e0e0e0 100%); 393 border-radius: var(--pc-radius); 394 } 395 396 /* Price formatting */ 397 .pc-price { 398 font-weight: 700; 399 color: var(--pc-primary-color); 400 } 401 402 /* Responsive Design */ 403 @media screen and (max-width: 1024px) { 404 .cluster .column3, 405 .pc-cluster .pc-item--card { 406 width: calc(50% - 20px); 407 } 408 409 .cluster.flex .columns, 410 .pc-cluster .pc-item--product { 411 flex: 1 1 calc(50% - 12px); 412 max-width: calc(50% - 12px); 413 } 414 } 415 416 @media screen and (max-width: 768px) { 417 .cluster .category, 418 .pc-cluster .pc-item--full { 419 grid-template-columns: 100px 1fr; 420 gap: 16px; 421 padding: 16px; 422 } 423 424 /* Legacy float override for mobile */ 425 .cluster .category .images { 426 float: none; 427 width: 100px; 428 padding: 0; 429 } 430 431 .cluster .category .articles { 432 float: none; 433 width: 100%; 434 padding: 0; 435 } 436 437 .cluster .column3, 438 .pc-cluster .pc-item--card { 439 width: 100%; 440 margin: 0 0 16px 0; 441 } 442 443 .cluster.flex, 444 .pc-cluster--flex { 445 gap: 16px; 446 } 447 448 .cluster.flex .columns, 449 .pc-cluster .pc-item--product { 450 flex: 1 1 100%; 451 max-width: 100%; 452 min-width: auto; 453 } 454 } 455 456 @media screen and (max-width: 480px) { 457 .cluster .category, 458 .pc-cluster .pc-item--full { 459 grid-template-columns: 1fr; 460 text-align: center; 461 } 462 463 .cluster .category .images, 464 .pc-cluster .pc-item--full .pc-item__image { 465 margin: 0 auto; 466 max-width: 150px; 467 } 468 469 .cluster.flex .columns .buttons, 470 .pc-cluster .pc-item--product .pc-item__buttons { 471 flex-direction: column; 472 } 473 474 .cluster.flex .columns .buttons a, 475 .cluster.flex .columns .buttons button, 476 .pc-cluster .pc-item--product .pc-btn { 477 width: 100%; 478 } 479 } 480 481 /* Print Styles */ 482 @media print { 483 .cluster, 484 .pc-cluster { 485 display: block; 486 } 487 488 .cluster .column3, 489 .cluster.flex .columns, 490 .pc-cluster .pc-item--card, 491 .pc-cluster .pc-item--product { 492 box-shadow: none; 493 border: 1px solid #ddd; 494 page-break-inside: avoid; 495 } 496 497 .cluster.flex .columns .buttons, 498 .pc-cluster .pc-item--product .pc-item__buttons { 499 display: none; 500 } 501 } 502 503 /* Accessibility */ 504 .cluster a:focus, 505 .pc-cluster a:focus, 506 .cluster button:focus, 507 .pc-cluster button:focus { 508 outline: 2px solid var(--pc-primary-color); 509 outline-offset: 2px; 510 } 511 512 /* Reduced motion support */ 513 @media (prefers-reduced-motion: reduce) { 514 .cluster *, 515 .pc-cluster * { 516 transition: none !important; 517 animation: none !important; 518 } 519 } -
pixel-clusters/trunk/js/cluster.js
r3405223 r3416780 3 3 jQuery(document).ready(function(){ 4 4 5 jQuery('#cat').on('change', function() { 6 pixel_cluster_code(); 7 }); 8 9 jQuery('#tag').on('change', function() { 5 jQuery('select').on('change', function() { 10 6 pixel_cluster_code(); 11 7 }); 12 8 13 9 }); 10 11 function pixel_cluster_buy_product( product_id ) 12 { 13 jQuery.get( '/wp-admin/admin-ajax.php' , { 14 'action' : 'woocommerce_ajax_add_to_cart', 15 'product_id' : product_id 16 }).done(function( data ) { 17 18 if(data == 'FAIL') 19 { 20 jQuery('#error-buy').html('<div class="error">We Have Experienced Technical Difficulties, Try again later.</div>'); 21 } 22 else if(data == 'DUPLICATE') 23 { 24 jQuery('#error-buy').html('<div class="error">You can not buy more than one test. Please proceed to cart.</div>'); 25 } 26 else 27 { 28 location.href = '/carro/'; 29 } 30 }); 31 } 32 33 function pixel_cluster_change_post() 34 { 35 var post_type = jQuery('#post_type').val(); 36 37 jQuery('.type-post').hide(); 38 39 jQuery('#type-'+ post_type).fadeIn(); 40 41 pixel_cluster_code(); 42 } 43 44 function pixel_cluster_change_taxonomy() 45 { 46 var post_type = jQuery('#post_type').val(); 47 var post_tax = jQuery('#post_tax_'+ post_type).val(); 48 49 jQuery('.type-tax').hide(); 50 51 jQuery('#typetax-'+ post_tax).fadeIn(); 52 53 pixel_cluster_code(); 54 } 14 55 15 56 function pixel_cluster_change_type() … … 29 70 var mode = pixel_cluster_variable( jQuery('#mode').val() ); 30 71 var number = pixel_cluster_variable( jQuery('#number').val() ); 72 var post_type = pixel_cluster_variable( jQuery('#post_type').val() ); 31 73 var tag_id = ''; 74 var taxonomy = ''; 32 75 33 76 if(type == 1) … … 39 82 tag_id = pixel_cluster_variable( jQuery('#tag').val() ); 40 83 } 84 else if(type == 3) 85 { 86 var taxonomy = pixel_cluster_variable( jQuery('#post_tax_'+ post_type).val() ); 87 tag_id = pixel_cluster_variable( jQuery('#term_'+ taxonomy).val() ); 88 } 89 else if(type == 4) 90 { 91 tag_id = pixel_cluster_variable( jQuery('#product_cat').val() ); 92 } 93 else if(type == 5) 94 { 95 tag_id = pixel_cluster_variable( jQuery('#product_tag').val() ); 96 } 41 97 42 jQuery('#code-generate').html('[cluster type="'+ type +'" tag_id="'+ tag_id +'" modo="'+ mode +'" numero="'+ number +'"]'); 98 jQuery('#code-generate').html('[cluster type="'+ type +'" tag_id="'+ tag_id +'" modo="'+ mode +'" numero="'+ number +'"'); 99 100 if(type == 3) 101 { 102 jQuery('#code-generate').append(' post_type="'+ post_type +'" taxonomy="'+ taxonomy +'"'); 103 } 104 105 jQuery('#code-generate').append(']'); 43 106 } 44 107 45 108 function pixel_cluster_variable(data) 46 109 { 47 if(data === null )110 if(data === null || data === undefined) 48 111 { 49 112 data = ''; -
pixel-clusters/trunk/pixel-clusters.php
r3405223 r3416780 1 1 <?php 2 2 /** 3 * Pixel Clusters - A modern plugin for creating post clusters 3 4 * 4 5 * @since 1.0.0 … … 8 9 * Plugin Name: Pixel Clusters 9 10 * Plugin URI: https://mireunion.com/ 10 * Description: A small plugin for create clusters of posts.11 * Version: 1.0.811 * Description: A modern plugin for creating beautiful clusters of posts, categories, tags, and WooCommerce products with responsive design. Includes Gutenberg block support. 12 * Version: 2.1.0 12 13 * Author: Mex Avila 13 14 * Author URI: https://datakun.com/ … … 16 17 * Text Domain: pixel-cluster 17 18 * Domain Path: /languages 18 * Network: true 19 * Network: true 20 * Requires at least: 6.4 21 * Tested up to: 6.9 22 * Requires PHP: 8.0 19 23 * 20 24 * This program is free software: you can redistribute it and/or modify … … 31 35 * along with this program. If not, see <http://www.gnu.org/licenses/>. 32 36 */ 33 37 34 38 if ( ! defined( 'ABSPATH' ) ) { 35 die( 'Invalid request.' ); 39 exit; // Exit if accessed directly 36 40 } 37 41 38 define( 'CLUSTER_PLUGIN_PATH', plugin_dir_path( __FILE__ ) ); 39 define( 'CLUSTER_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); 40 41 function pixel_cluster( $atts ) 42 { 43 global $post; 44 45 $html = '<aside class="cluster">'; 46 47 $a = shortcode_atts( array( 48 'type' => 1, 49 'tag_id' => 1, 50 'numero' => 4, 51 'modo' => 1, 52 ), $atts ); 53 54 55 $args = array( 56 'posts_per_page' => $a['numero'], 57 'orderby' => 'date', 58 'order' => 'DESC', 59 'post_type' => 'post', 60 'post_status' => 'publish', 61 'post__not_in' => array($post->ID) 62 ); 63 64 if($a['type'] == 1) 65 { 66 $args['cat'] = $a['tag_id']; 67 } 68 else 69 { 70 $args['tag_id'] = $a['tag_id']; 71 } 72 73 $posts = get_posts($args); 74 75 foreach($posts as $data) 76 { 77 switch($a['modo']) 78 { 79 case 1: 80 81 $html .= '<article class="category"> 82 <div class="images">'; 83 if ( has_post_thumbnail($data->ID) ) : 84 $html .= '<a href="'. esc_url( get_the_permalink($data->ID) ) .'" title="'. esc_attr( get_the_title($data->ID) ) .'"> 85 '. get_the_post_thumbnail($data->ID, 'thumbnail') .' 86 </a>'; 87 endif; 88 $html .= ' 89 </div> 90 <div class="articles"> 91 <div class="h2"><a href="'. esc_url( get_the_permalink($data->ID) ) .'">'. esc_html( get_the_title($data->ID) ) .'</a></div> 92 '. wp_kses_post( get_the_excerpt($data->ID) ) .' 93 </div> 94 </article>'; break; 95 96 case 2: 97 98 $html .= '<article class="column3"> 99 <a href="'. esc_url( get_the_permalink($data->ID) ) .'" title="'. esc_attr( get_the_title($data->ID) ) .'"><div class="images" style="background-image: url('. esc_url( get_the_post_thumbnail_url($data->ID, 'medium') ) .');"> </div></a> 100 <div class="articles"> 101 <div class="h2"><a href="'. esc_url( get_the_permalink($data->ID) ) .'">'. esc_html( get_the_title($data->ID) ) .'</a></div> 102 </div> 103 </article>'; 104 105 break; 106 107 case 3: 108 109 $html .= '<li><a href="'. esc_url( get_the_permalink($data->ID) ) .'">'. esc_html( get_the_title($data->ID) ) .'</a></li>'; 110 111 break; 112 } 113 } 114 wp_reset_postdata(); 115 116 $html .= '</aside>'; 117 118 return $html; 42 // Plugin Constants 43 define( 'PIXEL_CLUSTER_VERSION', '2.1.0' ); 44 define( 'PIXEL_CLUSTER_PATH', plugin_dir_path( __FILE__ ) ); 45 define( 'PIXEL_CLUSTER_URL', plugin_dir_url( __FILE__ ) ); 46 define( 'PIXEL_CLUSTER_BASENAME', plugin_basename( __FILE__ ) ); 47 48 // Legacy constants for backward compatibility 49 define( 'CLUSTER_PLUGIN_PATH', PIXEL_CLUSTER_PATH ); 50 define( 'CLUSTER_PLUGIN_URL', PIXEL_CLUSTER_URL ); 51 52 // Include Gutenberg Block 53 require_once PIXEL_CLUSTER_PATH . 'blocks/cluster-block.php'; 54 55 /** 56 * Main Pixel Cluster Class 57 */ 58 final class Pixel_Cluster { 59 60 /** 61 * Instance 62 * @var Pixel_Cluster 63 */ 64 private static $instance = null; 65 66 /** 67 * Get instance 68 */ 69 public static function get_instance() { 70 if ( null === self::$instance ) { 71 self::$instance = new self(); 72 } 73 return self::$instance; 74 } 75 76 /** 77 * Constructor 78 */ 79 private function __construct() { 80 $this->init_hooks(); 81 } 82 83 /** 84 * Initialize hooks 85 */ 86 private function init_hooks() { 87 // Admin menu 88 add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); 89 90 // Enqueue scripts 91 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); 92 add_action( 'wp_enqueue_scripts', array( $this, 'frontend_enqueue_scripts' ) ); 93 94 // Shortcode 95 add_shortcode( 'cluster', array( $this, 'render_shortcode' ) ); 96 97 // AJAX handlers 98 add_action( 'wp_ajax_pixel_cluster_preview', array( $this, 'ajax_preview' ) ); 99 add_action( 'wp_ajax_pixel_cluster_get_taxonomies', array( $this, 'ajax_get_taxonomies' ) ); 100 add_action( 'wp_ajax_pixel_cluster_get_terms', array( $this, 'ajax_get_terms' ) ); 101 add_action( 'wp_ajax_woocommerce_ajax_add_to_cart', array( $this, 'ajax_add_to_cart' ) ); 102 add_action( 'wp_ajax_nopriv_woocommerce_ajax_add_to_cart', array( $this, 'ajax_add_to_cart' ) ); 103 104 // Plugin action links 105 add_filter( 'plugin_action_links_' . PIXEL_CLUSTER_BASENAME, array( $this, 'plugin_action_links' ) ); 106 } 107 108 /** 109 * Add admin menu 110 */ 111 public function add_admin_menu() { 112 add_menu_page( 113 __( 'Pixel Clusters', 'pixel-cluster' ), 114 __( 'Clusters', 'pixel-cluster' ), 115 'manage_options', 116 'pixel-clusters', 117 array( $this, 'render_admin_page' ), 118 'dashicons-grid-view', 119 30 120 ); 121 } 122 123 /** 124 * Admin enqueue scripts 125 */ 126 public function admin_enqueue_scripts( $hook ) { 127 if ( 'toplevel_page_pixel-clusters' !== $hook ) { 128 return; 129 } 130 131 // CSS 132 wp_enqueue_style( 133 'pixel-clusters-admin', 134 PIXEL_CLUSTER_URL . 'css/admin.css', 135 array(), 136 PIXEL_CLUSTER_VERSION 137 ); 138 139 // JS 140 wp_enqueue_script( 141 'pixel-clusters-admin', 142 PIXEL_CLUSTER_URL . 'js/admin.js', 143 array( 'jquery' ), 144 PIXEL_CLUSTER_VERSION, 145 true 146 ); 147 148 // Localize script 149 wp_localize_script( 'pixel-clusters-admin', 'pixelClustersAdmin', array( 150 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 151 'nonce' => wp_create_nonce( 'pixel_cluster_nonce' ), 152 'i18n' => array( 153 'copied' => __( 'Shortcode copied!', 'pixel-cluster' ), 154 'copyError' => __( 'Error copying shortcode', 'pixel-cluster' ), 155 'previewError' => __( 'Error loading preview', 'pixel-cluster' ), 156 ) 157 ) ); 158 } 159 160 /** 161 * Frontend enqueue scripts 162 */ 163 public function frontend_enqueue_scripts() { 164 wp_enqueue_style( 165 'pixel-clusters', 166 PIXEL_CLUSTER_URL . 'css/pixel-clusters.css', 167 array(), 168 PIXEL_CLUSTER_VERSION 169 ); 170 171 // Only load JS if WooCommerce is active 172 if ( class_exists( 'WooCommerce' ) ) { 173 wp_enqueue_script( 174 'pixel-clusters', 175 PIXEL_CLUSTER_URL . 'js/cluster.js', 176 array( 'jquery' ), 177 PIXEL_CLUSTER_VERSION, 178 true 179 ); 180 181 wp_localize_script( 'pixel-clusters', 'pixelClustersFront', array( 182 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 183 'nonce' => wp_create_nonce( 'pixel_cluster_front_nonce' ), 184 'cartUrl' => function_exists( 'wc_get_cart_url' ) ? wc_get_cart_url() : '/cart/', 185 ) ); 186 } 187 } 188 189 /** 190 * Render shortcode 191 */ 192 public function render_shortcode( $atts ) { 193 global $post; 194 195 $atts = shortcode_atts( array( 196 'type' => 1, 197 'tag_id' => '', 198 'numero' => 4, 199 'modo' => 1, 200 'post_type' => 'post', 201 'taxonomy' => '' 202 ), $atts, 'cluster' ); 203 204 $args = array( 205 'posts_per_page' => absint( $atts['numero'] ), 206 'orderby' => 'date', 207 'order' => 'DESC', 208 'post_status' => 'publish', 209 'post__not_in' => $post ? array( $post->ID ) : array() 210 ); 211 212 // Build query based on type 213 switch ( absint( $atts['type'] ) ) { 214 case 1: // Categories 215 if ( ! empty( $atts['tag_id'] ) ) { 216 $args['cat'] = absint( $atts['tag_id'] ); 217 } 218 $args['post_type'] = sanitize_text_field( $atts['post_type'] ); 219 break; 220 221 case 2: // Tags 222 if ( ! empty( $atts['tag_id'] ) ) { 223 $args['tag_id'] = absint( $atts['tag_id'] ); 224 } 225 $args['post_type'] = sanitize_text_field( $atts['post_type'] ); 226 break; 227 228 case 3: // Custom Post Type 229 $args['post_type'] = sanitize_text_field( $atts['post_type'] ); 230 if ( ! empty( $atts['tag_id'] ) && ! empty( $atts['taxonomy'] ) ) { 231 $args['tax_query'] = array( 232 array( 233 'taxonomy' => sanitize_text_field( $atts['taxonomy'] ), 234 'field' => 'term_id', 235 'terms' => absint( $atts['tag_id'] ), 236 ), 237 ); 238 } 239 break; 240 241 case 4: // WooCommerce Categories 242 $args['post_type'] = 'product'; 243 if ( ! empty( $atts['tag_id'] ) ) { 244 $args['tax_query'] = array( 245 array( 246 'taxonomy' => 'product_cat', 247 'field' => 'term_id', 248 'terms' => absint( $atts['tag_id'] ), 249 ), 250 ); 251 } 252 break; 253 254 case 5: // WooCommerce Tags 255 $args['post_type'] = 'product'; 256 if ( ! empty( $atts['tag_id'] ) ) { 257 $args['tax_query'] = array( 258 array( 259 'taxonomy' => 'product_tag', 260 'field' => 'term_id', 261 'terms' => absint( $atts['tag_id'] ), 262 ), 263 ); 264 } 265 break; 266 } 267 268 $posts = get_posts( $args ); 269 270 if ( empty( $posts ) ) { 271 return ''; 272 } 273 274 // Generate HTML based on mode 275 return $this->generate_cluster_html( $posts, absint( $atts['modo'] ) ); 276 } 277 278 /** 279 * Generate cluster HTML 280 */ 281 private function generate_cluster_html( $posts, $mode ) { 282 // Use both old and new classes for backward compatibility 283 $classes = 'cluster pc-cluster pc-cluster--mode-' . $mode; 284 if ( 4 === $mode ) { 285 $classes .= ' flex pc-cluster--flex'; 286 } 287 288 $html = '<aside class="' . esc_attr( $classes ) . '">'; 289 290 foreach ( $posts as $post_item ) { 291 $html .= $this->generate_item_html( $post_item, $mode ); 292 } 293 294 wp_reset_postdata(); 295 $html .= '</aside>'; 296 297 return $html; 298 } 299 300 /** 301 * Generate single item HTML 302 */ 303 private function generate_item_html( $post_item, $mode ) { 304 $permalink = get_the_permalink( $post_item->ID ); 305 $title = get_the_title( $post_item->ID ); 306 $thumbnail_url = get_the_post_thumbnail_url( $post_item->ID, 'medium' ); 307 308 switch ( $mode ) { 309 case 1: // Image, title and excerpt 310 return sprintf( 311 '<article class="category pc-item pc-item--full"> 312 <div class="images pc-item__image"> 313 %s 314 </div> 315 <div class="articles pc-item__content"> 316 <div class="h2 pc-item__title"> 317 <a href="%s">%s</a> 318 </div> 319 <div class="pc-item__excerpt">%s</div> 320 </div> 321 </article>', 322 has_post_thumbnail( $post_item->ID ) 323 ? sprintf( 324 '<a href="%s" title="%s">%s</a>', 325 esc_url( $permalink ), 326 esc_attr( $title ), 327 get_the_post_thumbnail( $post_item->ID, 'thumbnail', array( 'class' => 'pc-item__img' ) ) 328 ) 329 : '<span class="pc-item__placeholder"></span>', 330 esc_url( $permalink ), 331 esc_html( $title ), 332 wp_kses_post( get_the_excerpt( $post_item->ID ) ) 333 ); 334 335 case 2: // Image and title only 336 return sprintf( 337 '<article class="column3 pc-item pc-item--card"> 338 <a href="%s" title="%s" class="pc-item__image-link"> 339 <div class="images pc-item__image-bg" style="background-image: url(%s);"></div> 340 </a> 341 <div class="articles pc-item__content"> 342 <div class="h2 pc-item__title"> 343 <a href="%s">%s</a> 344 </div> 345 </div> 346 </article>', 347 esc_url( $permalink ), 348 esc_attr( $title ), 349 esc_url( $thumbnail_url ?: '' ), 350 esc_url( $permalink ), 351 esc_html( $title ) 352 ); 353 354 case 3: // List only 355 return sprintf( 356 '<li class="pc-item pc-item--list"> 357 <a href="%s">%s</a> 358 </li>', 359 esc_url( $permalink ), 360 esc_html( $title ) 361 ); 362 363 case 4: // Products 364 if ( ! class_exists( 'WooCommerce' ) ) { 365 return ''; 366 } 367 368 $product = wc_get_product( $post_item->ID ); 369 if ( ! $product || 'instock' !== $product->get_stock_status() ) { 370 return ''; 371 } 372 373 return sprintf( 374 '<article class="columns pc-item pc-item--product"> 375 <a href="%s" title="%s" class="pc-item__image-link"> 376 <div class="images pc-item__image-bg" style="background-image: url(%s);"></div> 377 </a> 378 <div class="articles pc-item__content"> 379 <div class="h2 pc-item__title"> 380 <a href="%s">%s</a> 381 </div> 382 <div class="price pc-item__price">%s</div> 383 <div class="buttons pc-item__buttons"> 384 <a href="%s" class="pc-btn pc-btn--view">%s</a> 385 <button type="button" class="carrito pc-btn pc-btn--cart" data-product-id="%d" onclick="pixelClusterBuyProduct(%d)">%s</button> 386 </div> 387 </div> 388 </article>', 389 esc_url( $permalink ), 390 esc_attr( $title ), 391 esc_url( $thumbnail_url ?: '' ), 392 esc_url( $permalink ), 393 esc_html( $title ), 394 $this->format_price( $product->get_price() ), 395 esc_url( $permalink ), 396 esc_html__( 'View more', 'pixel-cluster' ), 397 absint( $post_item->ID ), 398 absint( $post_item->ID ), 399 esc_html__( 'Add to cart', 'pixel-cluster' ) 400 ); 401 402 default: 403 return ''; 404 } 405 } 406 407 /** 408 * Format price 409 */ 410 private function format_price( $price ) { 411 if ( function_exists( 'wc_price' ) ) { 412 return wc_price( $price ); 413 } 414 415 return '<span class="pc-price">' . esc_html( number_format( (float) $price, 2 ) ) . '</span>'; 416 } 417 418 /** 419 * Get post types 420 */ 421 public function get_post_types() { 422 $post_types = get_post_types( array( 423 'public' => true, 424 '_builtin' => false 425 ), 'objects' ); 426 427 $exclude = array( 'product', 'attachment' ); 428 $result = array( 429 'types' => array(), 430 'taxonomies' => array() 431 ); 432 433 foreach ( $post_types as $post_type ) { 434 if ( in_array( $post_type->name, $exclude, true ) ) { 435 continue; 436 } 437 438 $result['types'][ $post_type->name ] = $post_type->label; 439 $result['taxonomies'][ $post_type->name ] = array(); 440 441 $taxonomies = get_object_taxonomies( $post_type->name, 'objects' ); 442 foreach ( $taxonomies as $taxonomy ) { 443 $result['taxonomies'][ $post_type->name ][ $taxonomy->name ] = $taxonomy->label; 444 } 445 } 446 447 return $result; 448 } 449 450 /** 451 * Render admin page 452 */ 453 public function render_admin_page() { 454 require_once PIXEL_CLUSTER_PATH . 'content/pixel-html.php'; 455 } 456 457 /** 458 * AJAX: Preview shortcode 459 */ 460 public function ajax_preview() { 461 check_ajax_referer( 'pixel_cluster_nonce', 'nonce' ); 462 463 if ( ! current_user_can( 'manage_options' ) ) { 464 wp_send_json_error( array( 'message' => __( 'Unauthorized', 'pixel-cluster' ) ) ); 465 } 466 467 $shortcode = isset( $_POST['shortcode'] ) ? sanitize_text_field( wp_unslash( $_POST['shortcode'] ) ) : ''; 468 469 if ( empty( $shortcode ) ) { 470 wp_send_json_error( array( 'message' => __( 'Invalid shortcode', 'pixel-cluster' ) ) ); 471 } 472 473 $html = do_shortcode( $shortcode ); 474 475 if ( empty( $html ) ) { 476 wp_send_json_error( array( 'message' => __( 'No posts found', 'pixel-cluster' ) ) ); 477 } 478 479 wp_send_json_success( array( 'html' => $html ) ); 480 } 481 482 /** 483 * AJAX: Get taxonomies 484 */ 485 public function ajax_get_taxonomies() { 486 check_ajax_referer( 'pixel_cluster_nonce', 'nonce' ); 487 488 $post_type = isset( $_POST['post_type'] ) ? sanitize_text_field( wp_unslash( $_POST['post_type'] ) ) : ''; 489 490 if ( empty( $post_type ) ) { 491 wp_send_json_error(); 492 } 493 494 $taxonomies = get_object_taxonomies( $post_type, 'objects' ); 495 $result = array(); 496 497 foreach ( $taxonomies as $taxonomy ) { 498 $result[] = array( 499 'name' => $taxonomy->name, 500 'label' => $taxonomy->label 501 ); 502 } 503 504 wp_send_json_success( $result ); 505 } 506 507 /** 508 * AJAX: Get terms 509 */ 510 public function ajax_get_terms() { 511 check_ajax_referer( 'pixel_cluster_nonce', 'nonce' ); 512 513 $taxonomy = isset( $_POST['taxonomy'] ) ? sanitize_text_field( wp_unslash( $_POST['taxonomy'] ) ) : ''; 514 515 if ( empty( $taxonomy ) ) { 516 wp_send_json_error(); 517 } 518 519 $terms = get_terms( array( 520 'taxonomy' => $taxonomy, 521 'hide_empty' => true, 522 'orderby' => 'name', 523 'order' => 'ASC' 524 ) ); 525 526 $result = array(); 527 528 if ( ! is_wp_error( $terms ) ) { 529 foreach ( $terms as $term ) { 530 $result[] = array( 531 'id' => $term->term_id, 532 'name' => $term->name 533 ); 534 } 535 } 536 537 wp_send_json_success( $result ); 538 } 539 540 /** 541 * AJAX: Add to cart 542 */ 543 public function ajax_add_to_cart() { 544 if ( ! class_exists( 'WooCommerce' ) ) { 545 echo 'FAIL'; 546 wp_die(); 547 } 548 549 $product_id = isset( $_REQUEST['product_id'] ) ? absint( $_REQUEST['product_id'] ) : 0; 550 $quantity = isset( $_REQUEST['quantity'] ) ? wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ) : 1; 551 $variation_id = isset( $_REQUEST['variation_id'] ) ? absint( $_REQUEST['variation_id'] ) : 0; 552 553 $product_status = get_post_status( $product_id ); 554 $passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity ); 555 556 if ( $passed_validation && WC()->cart->add_to_cart( $product_id, $quantity, $variation_id ) && 'publish' === $product_status ) { 557 do_action( 'woocommerce_ajax_added_to_cart', $product_id ); 558 559 if ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) { 560 wc_add_to_cart_message( array( $product_id => $quantity ), true ); 561 } 562 563 WC_AJAX::get_refreshed_fragments(); 564 echo 'GOCART'; 565 } else { 566 // Check if product already in cart 567 $in_cart = false; 568 foreach ( WC()->cart->get_cart() as $cart_item ) { 569 if ( $cart_item['product_id'] === $product_id ) { 570 $in_cart = true; 571 break; 572 } 573 } 574 575 echo $in_cart ? 'DUPLICATE' : 'FAIL'; 576 } 577 578 wp_die(); 579 } 580 581 /** 582 * Plugin action links 583 */ 584 public function plugin_action_links( $links ) { 585 $settings_link = sprintf( 586 '<a href="%s">%s</a>', 587 admin_url( 'admin.php?page=pixel-clusters' ), 588 __( 'Settings', 'pixel-cluster' ) 589 ); 590 591 array_unshift( $links, $settings_link ); 592 593 return $links; 594 } 119 595 } 120 add_shortcode( 'cluster', 'pixel_cluster' ); 121 122 123 function pixel_cluster_admin() 124 { 125 add_menu_page( 'Clusters', 'Clusters', 'manage_options', 'clusters', 'safetya_cluster_page' ); 596 597 // Legacy function for backward compatibility 598 function pixel_cluster( $atts ) { 599 return Pixel_Cluster::get_instance()->render_shortcode( $atts ); 126 600 } 127 add_action( 'admin_menu', 'pixel_cluster_admin' ); 128 129 function pixel_cluster_admin_enqueue($hook) 130 { 131 if($hook != 'toplevel_page_clusters') { 132 return; 133 } 134 135 wp_enqueue_script( 'pixel-clusters-js', CLUSTER_PLUGIN_URL . 'js/cluster.js', array ( 'jquery' ), 1.0, true); 601 602 // Legacy function for post types 603 function pixel_cluster_post_type() { 604 $data = Pixel_Cluster::get_instance()->get_post_types(); 605 return array( 606 'name_type' => array_keys( $data['types'] ), 607 'name_taxonomies' => $data['taxonomies'] 608 ); 136 609 } 137 add_action( 'admin_enqueue_scripts', 'pixel_cluster_admin_enqueue' ); 138 139 function pixel_cluster_init() 140 { 141 if ( ! is_admin() ) 142 { 143 wp_enqueue_style( 'pixel-clusters', CLUSTER_PLUGIN_URL .'css/pixel-clusters.css' ); 144 } 145 else 146 { 147 wp_enqueue_script( 'pixel-clusters-js', CLUSTER_PLUGIN_URL . 'js/cluster.js', array ( 'jquery' ), 1.0, true); 148 } 610 611 // Legacy money function 612 function pixel_cluster_money( $money, $print = true ) { 613 if ( function_exists( 'wc_price' ) ) { 614 $formatted = wc_price( $money ); 615 } else { 616 $formatted = '<nobr>' . number_format( (float) $money, 2 ) . '</nobr>'; 617 } 618 619 if ( $print ) { 620 echo wp_kses_post( $formatted ); 621 } else { 622 return $formatted; 623 } 149 624 } 150 add_action('wp_enqueue_scripts', 'pixel_cluster_init'); 151 152 function safetya_cluster_page() 153 { 154 require_once( CLUSTER_PLUGIN_PATH . 'content/pixel-html.php' ); 155 } 625 626 // Initialize plugin 627 Pixel_Cluster::get_instance(); -
pixel-clusters/trunk/readme.txt
r3405223 r3416780 1 === Pixel Cluster ===1 === Pixel Clusters === 2 2 Contributors: kakaroto84 3 Tags: cluster, posts, categories, tags, pixel4 3 Donate link: https://www.paypal.me/datakun 4 Tags: cluster, posts, categories, tags, shortcode, responsive, woocommerce, custom post type 5 5 Requires at least: 6.4 6 6 Tested up to: 6.9 7 7 Requires PHP: 8.0 8 Stable tag: trunk8 Stable tag: 2.1.0 9 9 License: GPLv3 or later 10 10 License URI: http://www.gnu.org/licenses/gpl-3.0.html 11 11 12 A small plugin for create clusters of posts.12 Create beautiful, responsive post clusters with shortcodes or Gutenberg blocks. Display posts, categories, tags, custom post types, and WooCommerce products in elegant layouts. 13 13 14 14 == Description == 15 You can create your clusters an easy way. 16 17 1. Go to Clusters 18 2. Select the options 19 3. Copy the shortcode 20 4. Paste in your post/page 15 16 **Pixel Clusters** is a powerful yet simple WordPress plugin that allows you to create beautiful clusters of posts, categories, tags, and products using shortcodes. 17 18 = 🚀 Key Features = 19 20 * **Gutenberg Block**: Native block editor support with live preview 21 * **4 Display Modes**: Choose from full articles, card layouts, simple lists, or product grids 22 * **Multiple Content Sources**: Works with categories, tags, custom post types, and WooCommerce 23 * **Fully Responsive**: Looks great on all devices - desktop, tablet, and mobile 24 * **Modern Admin Interface**: Clean, AJAX-powered admin panel with live preview 25 * **WooCommerce Integration**: Display products with prices and add-to-cart buttons 26 * **Custom Post Type Support**: Works with any registered custom post type and taxonomy 27 * **Easy to Use**: Simple shortcode generator or Gutenberg block - no coding required 28 * **Performance Optimized**: Lightweight and fast-loading 29 * **Accessibility Ready**: Full keyboard navigation and screen reader support 30 31 = 🎨 Display Modes = 32 33 1. **Full Article** - Image, title, and excerpt in a clean layout 34 2. **Cards** - Beautiful card grid with images and titles 35 3. **List** - Simple, elegant list of post titles 36 4. **Products** - WooCommerce products with prices and cart buttons 37 38 = 📝 How to Use = 39 40 **Using Gutenberg Block (Recommended):** 41 42 1. Open the Block Editor in any post or page 43 2. Click the + button to add a block 44 3. Search for "Pixel Cluster" or find it in the Widgets category 45 4. Configure settings in the block sidebar 46 5. See live preview directly in the editor 47 48 **Using Shortcode Generator:** 49 50 1. Go to **Clusters** in your WordPress admin menu 51 2. Select your preferred display mode 52 3. Choose the content type (categories, tags, custom post type, etc.) 53 4. Set the number of posts to display 54 5. Copy the generated shortcode 55 6. Paste it in any post, page, or widget 56 57 = 💡 Example Shortcodes = 58 59 Display 4 posts from a category: 60 `[cluster type="1" tag_id="5" modo="1" numero="4"]` 61 62 Display products from WooCommerce: 63 `[cluster type="4" modo="4" numero="6"]` 64 65 Display custom post type: 66 `[cluster type="3" post_type="portfolio" taxonomy="portfolio_cat" tag_id="10" modo="2" numero="3"]` 67 68 = 🔧 Shortcode Parameters = 69 70 * **type** - Content source: 1=Categories, 2=Tags, 3=Custom Post Type, 4=WooCommerce Categories, 5=WooCommerce Tags 71 * **tag_id** - The term ID to filter by (optional) 72 * **modo** - Display mode: 1=Full, 2=Cards, 3=List, 4=Products 73 * **numero** - Number of posts to display (default: 4) 74 * **post_type** - Custom post type slug (for type=3) 75 * **taxonomy** - Custom taxonomy slug (for type=3) 21 76 22 77 == Installation == 23 Install Pixel Cluster like you would install any other WordPress plugin. 24 25 Dashboard Method: 26 27 1. Login to your WordPress admin and go to Plugins -> Add New 28 2. Type \"Pixel Cluster\" in the search bar and select this plugin 29 3. Click \"Install\", and then \"Activate Plugin\" 30 31 32 Upload Method: 33 34 1. Unzip the plugin and upload the \"pixel-cluster\" folder to your \'wp-content/plugins\' directory 35 2. Activate the plugin through the Plugins menu in WordPress 78 79 = Automatic Installation = 80 81 1. Go to **Plugins → Add New** in your WordPress admin 82 2. Search for "Pixel Clusters" 83 3. Click **Install Now** and then **Activate** 84 85 = Manual Installation = 86 87 1. Download the plugin ZIP file 88 2. Go to **Plugins → Add New → Upload Plugin** 89 3. Choose the ZIP file and click **Install Now** 90 4. Activate the plugin 91 92 = FTP Installation = 93 94 1. Download and unzip the plugin 95 2. Upload the `pixel-clusters` folder to `/wp-content/plugins/` 96 3. Activate through the **Plugins** menu in WordPress 36 97 37 98 == Frequently Asked Questions == 38 = Works in customs posts? = 39 40 Yes, it works! 99 100 = Does it work with custom post types? = 101 102 Yes! Pixel Clusters fully supports custom post types and their taxonomies. Select "Custom Post Type" in the generator and choose your post type and taxonomy. 103 104 = Is it compatible with WooCommerce? = 105 106 Absolutely! Display your WooCommerce products with prices and add-to-cart buttons. Select mode 4 (Products) for the best WooCommerce experience. 107 108 = Can I customize the styling? = 109 110 Yes, all elements have CSS classes that you can target in your theme's stylesheet or the WordPress Customizer. 111 112 = Is it responsive? = 113 114 Yes! All display modes are fully responsive and look great on mobile devices, tablets, and desktops. 115 116 = Does it support Gutenberg? = 117 118 Yes! Pixel Clusters includes a native Gutenberg block with full support for: 119 * Live preview in the editor 120 * All display modes and content types 121 * Block alignment (wide and full width) 122 * Server-side rendering for accurate previews 123 124 Simply search for "Pixel Cluster" in the block inserter to add it to your content. 125 126 = Is it translation ready? = 127 128 Yes! The plugin is fully translatable and includes a POT file for translations. 41 129 42 130 == Screenshots == 43 1. Configure the shortcode 44 2. Copy / paste in post / page 45 3. How works 131 132 1. Modern admin interface with shortcode generator 133 2. Live preview feature 134 3. Documentation tab with parameter reference 135 4. Card layout display mode 136 5. Product grid with WooCommerce integration 137 6. Mobile responsive design 46 138 47 139 == Changelog == 140 141 = 2.1.0 = 142 **Gutenberg Block Support** 143 144 * **New**: Native Gutenberg block for the block editor 145 * **New**: Live preview in the block editor 146 * **New**: Block sidebar controls for all settings 147 * **New**: Support for wide and full width alignments 148 * **New**: Server-side rendering for accurate previews 149 * **New**: Dynamic category/tag/taxonomy loading 150 * **Improved**: Better integration with WordPress 6.9 block editor 151 152 = 2.0.0 = 153 **Major Update - Complete Redesign** 154 155 * **New**: Modern, AJAX-powered admin interface 156 * **New**: Live shortcode preview feature 157 * **New**: Tab-based navigation (Generator / Documentation) 158 * **New**: Toast notifications for user feedback 159 * **New**: CSS variables for easy customization 160 * **New**: Grid-based responsive layouts 161 * **New**: Improved accessibility (WCAG 2.1 compliant) 162 * **New**: Support for WordPress admin color schemes 163 * **Improved**: Complete CSS rewrite with modern standards 164 * **Improved**: Better WooCommerce integration 165 * **Improved**: Enhanced security with proper escaping and nonces 166 * **Improved**: Code refactored using OOP principles 167 * **Improved**: Performance optimizations 168 * **Improved**: Mobile responsive design 169 * **Fixed**: Various bug fixes and improvements 170 * **Compatibility**: Tested with WordPress 6.9 171 * **Compatibility**: Tested with PHP 8.0, 8.1, 8.2, and 8.3 172 48 173 = 1.0.8 = 49 174 * Tested and confirmed compatibility with WordPress 6.9 50 175 * Updated Clipboard API to modern standard 51 176 * Improved output escaping for security 52 = 1.0 = 53 * Initial release 54 = 1.0.1 = 55 * Add copy button 177 178 = 1.0.7 = 179 * Tested in last WP Version 180 181 = 1.0.6.1 = 182 * Change required version of PHP 183 184 = 1.0.6 = 185 * Tested in last WP Version 186 187 = 1.0.5 = 188 * Tested in last WP Version 189 190 = 1.0.4 = 191 * Tested in last WP Version 192 193 = 1.0.3 = 194 * Tested in last WP Version 195 56 196 = 1.0.2 = 57 197 * Tested new version of WP 58 = 1.0.3 = 59 * Tested in last WP Version 60 = 1.0.4 = 61 * Tested in last WP Version 62 = 1.0.5 = 63 * Tested in last WP Version 64 = 1.0.6 = 65 * Tested in last WP Version 66 = 1.0.6.1 = 67 * Change required version of PHP 68 = 1.0.7 = 69 * Tested in last WP Version 198 199 = 1.0.1 = 200 * Added copy button for shortcode 201 202 = 1.0.0 = 203 * Initial release 204 205 == Upgrade Notice == 206 207 = 2.1.0 = 208 New Gutenberg block support! Create post clusters directly in the block editor with live preview. Fully backward compatible with existing shortcodes. 209 210 = 2.0.0 = 211 Major update with completely redesigned admin interface, live preview, improved responsive design, and enhanced WooCommerce integration. Recommended update for all users. 212 213 == Privacy Policy == 214 215 Pixel Clusters does not collect, store, or share any personal data. The plugin does not use cookies, tracking, or external services.
Note: See TracChangeset
for help on using the changeset viewer.