Feature Request: Add “On Sale” Filter Option to Product Filters Widget

This topic has 6 replies, 2 voices, and was last updated 3 months, 2 weeks ago ago by Luca Rossi

  • Avatar: Juan Alberto Montoya
    Juan Alberto Montoya
    Participant
    August 15, 2025 at 14:29

    Hello,

    I’m using the Product Filters widget and I’d like to request a new feature or some guidance on how to implement it.

    I would like to add an “On Sale” filter option to the widget. This would allow users to easily see all products that are currently discounted. The ideal placement for this filter would be after the “Shipments” option.

    Could you please let me know if this is a feature you can add or if there is a way to achieve this functionality with the current theme/plugins?

    Thank you for your time and consideration.

    Please, contact administrator
    for this information.
    Files is visible for topic creator and
    support staff only.
    5 Answers
    Avatar: Justin
    Luca Rossi
    Support staff
    August 15, 2025 at 16:25

    Dear @Juan Alberto Montoya,

    We hope this message finds you well.

    Could you kindly try adding the Product Status Widget to the Shop Sidebar at your earliest convenience?

    For your reference, please find the following screenshots:
    https://prnt.sc/hpI4k2rjjo4M
    https://prnt.sc/1vJCiDwe6WCN

    We hope this information is helpful.
    Thank you in advance for your assistance.

    Best regards,
    The 8Theme Team

    Avatar: Juan Alberto Montoya
    Juan Alberto Montoya
    Participant
    August 15, 2025 at 19:06

    Hello,

    Sorry, your suggestion didn’t work for me because i’m using the “product filter” widget on elementor.

    Finally I was able to get the “on sale” filter working, but I did it by directly editing the plugin’s code for the “Product Filter” widget in Xstore “/wp-content/plugins/et-core-plugin/app/controllers/elementor/general/product-filters.php. ”

    Now I can see a new “Filter Type” option called “on sale” and it works perfectly.

    My concern is that my custom code will be erased the next time the plugin updates. Could you please tell me the best way to add this code so that my changes are permanent and update-safe?

    or can you implement this useful feature in your plugins updates with my code 🙂

    Thank you for your support.

    <?php
    namespace ETC\App\Controllers\Elementor\General;
    
    use ETC\App\Classes\Elementor;
    
    /**
     * Product filters widget.
     *
     * @since      4.0.8
     * @package    ETC
     * @subpackage ETC/Controllers/Elementor/General
     */
    
    class Product_Filters extends \Elementor\Widget_Base {
    	
    	/**
    	 * Get widget name.
    	 *
    	 * @since 4.0.8
    	 * @access public
    	 *
    	 * @return string Widget name.
    	 */
    	public function get_name() {
    		return 'etheme_product_filters';
    	}
    	
    	/**
    	 * Get widget title.
    	 *
    	 * @since 4.0.8
    	 * @access public
    	 *
    	 * @return string Widget title.
    	 */
    	public function get_title() {
    		return __( 'Product Filters', 'xstore-core' );
    	}
    	
    	/**
    	 * Get widget icon.
    	 *
    	 * @since 4.0.8
    	 * @access public
    	 *
    	 * @return string Widget icon.
    	 */
    	public function get_icon() {
    		return 'eight_theme-elementor-icon et-elementor-product-filter';
    	}
    	
    	/**
    	 * Get widget keywords.
    	 *
    	 * @since 4.0.8
    	 * @access public
    	 *
    	 * @return array Widget keywords.
    	 */
    	public function get_keywords() {
    		return [ 'product', 'filter', 'attributes', 'categories', 'price', 'select', 'woocommerce' ];
    	}
    	
    	/**
    	 * Get widget categories.
    	 *
    	 * @since 4.0.8
    	 * @access public
    	 *
    	 * @return array Widget categories.
    	 */
    	public function get_categories() {
    		return [ 'eight_theme_general' ];
    	}
    	
    	/**
    	 * Get widget dependency.
    	 *
    	 * @since 4.0.8
    	 * @access public
    	 *
    	 * @return array Widget dependency.
    	 */
    	
    	public function get_script_depends() {
            return apply_filters('etheme_elementor_widget_script_depends', ['etheme_product_filters'], true);
    	}
    	
    	public function get_style_depends() {
    		return apply_filters('etheme_elementor_widget_style_depends', [ 'etheme-elementor-product-filters' ], true);
    	}
    	
    	/**
    	 * Help link.
    	 *
    	 * @since 4.1.5
    	 *
    	 * @return string
    	 */
    	public function get_custom_help_url() {
    		return etheme_documentation_url('122-elementor-live-copy-option', false);
    	}
    	
    	/**
    	 * Register the widget controls.
    	 *
    	 * @since 4.0.8
    	 * @access protected
    	 */
    	protected function register_controls() {
    	    if ( !class_exists('WooCommerce') ) {
    		    $this->start_controls_section(
    			    'section_general',
    			    [
    				    'label' => esc_html__( 'General', 'xstore-core' ),
    			    ]
    		    );
    		    $this->add_control(
    			    'required_plugin_info',
    			    [
    				    'type'            => \Elementor\Controls_Manager::RAW_HTML,
    				    'raw' => sprintf( __( 'Please, install <a href="%s" target="_blank">WooCommerce</a> plugin to use this widget', 'xstore-core' ), admin_url('plugin-install.php?s=woocommerce&tab=search&type=term') ),
    				    'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
    			    ]
    		    );
    		    $this->end_controls_section();
            }
    	    else {
    		    $this->start_controls_section(
    			    'section_general',
    			    [
    				    'label' => esc_html__( 'General', 'xstore-core' ),
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'type',
    			    [
    				    'label'   => esc_html__( 'Type', 'xstore-core' ),
    				    'type'    => \Elementor\Controls_Manager::SELECT,
    				    'options' => array(
    					    'separated' => esc_html__('Separated', 'xstore-core'),
    					    'inline' => esc_html__('Inline', 'xstore-core')
    				    ),
    				    'default' => 'separated',
    			    ]
    		    );
    
                $this->add_control(
                    'content_position',
                    [
                        'label'   => esc_html__( 'Content Position', 'xstore-core' ),
                        'type'    => \Elementor\Controls_Manager::SELECT,
                        'options' => array(
                            'top' => esc_html__('Top', 'xstore-core'),
                            '' => esc_html__('Bottom', 'xstore-core')
                        ),
                        'default' => '',
                        'prefix_class'          => 'etheme-product-filters-content-',
                    ]
                );
    		
    		    $repeater = new \Elementor\Repeater();
    		
    		    $filtered_taxonomies = $this->product_taxonomies_to_filter();
    		
    		    $repeater_options = array(
    			    'attributes' => esc_html__( 'Attributes', 'xstore-core' ),
    			    'price'      => esc_html__( 'Price', 'xstore-core' ),
    			    'rating'     => esc_html__( 'Rating', 'xstore-core' ),
    			    'orderby'    => esc_html__( 'Order By', 'xstore-core' ),
    			    'on_sale'    => esc_html__( 'On Sale', 'xstore-core' ), // <-- Añade esta línea
    		    );
    		
    		    $repeater_options = $filtered_taxonomies + $repeater_options;
    		
    		    $repeater->add_control(
    			    'filter_title',
    			    [
    				    'label'     => esc_html__( 'Title', 'xstore-core' ),
    				    'type'      => \Elementor\Controls_Manager::TEXT,
    				    'default'   => esc_html__('Filter','xstore-core')
    			    ]
    		    );
    		
    		    $repeater->add_control(
    			    'filter_type',
    			    [
    				    'label'   => esc_html__( 'Filter type', 'xstore-core' ),
    				    'type'    => \Elementor\Controls_Manager::SELECT,
    				    'options' => $repeater_options,
    				    'default' => array_key_exists('product_cat', $repeater_options) ? 'product_cat' : $repeater_options[array_key_first($repeater_options)],
    			    ]
    		    );
    		
    		    foreach ($filtered_taxonomies as $taxonomy_key => $taxonomy_title) {
    			    $taxonomy = get_taxonomy($taxonomy_key);
    			
    			    if ( $taxonomy->hierarchical ) {
    				    $repeater->add_control(
    					    $taxonomy_key . '_hierarchical',
    					    [
    						    'label'     => esc_html__( 'Show Hierarchy', 'xstore-core' ),
    						    'type'      => \Elementor\Controls_Manager::SWITCHER,
    						    'condition' => [
    							    'filter_type' => $taxonomy_key,
    						    ],
    					    ]
    				    );
    			    }
    			
    			    $repeater->add_control(
    				    $taxonomy_key.'_hide_empty',
    				    [
    					    'label'        => esc_html__( 'Hide Empty', 'xstore-core' ),
    					    'type'         => \Elementor\Controls_Manager::SWITCHER,
    					    'condition'    => [
    						    'filter_type' => $taxonomy_key,
    					    ],
    				    ]
    			    );
    			
    			    $repeater->add_control(
    				    $taxonomy_key.'_show_count',
    				    [
    					    'label'        => esc_html__( 'Show Count', 'xstore-core' ),
    					    'type'         => \Elementor\Controls_Manager::SWITCHER,
    					    'condition'    => [
    						    'filter_type' => $taxonomy_key,
    					    ],
    				    ]
    			    );
    		    }
    		
    		    $repeater->add_control(
    			    'attribute',
    			    [
    				    'label'     => esc_html__( 'Attribute', 'xstore-core' ),
    				    'type'      => \Elementor\Controls_Manager::SELECT,
    				    'options'   => $this->get_attributes(),
    				    'default'   => '',
    				    'condition' => [
    					    'filter_type' => 'attributes',
    				    ],
    			    ]
    		    );
    
                $repeater->add_control(
                     'rating_type',
                    [
                        'label'     => esc_html__( 'Display Type', 'xstore-core' ),
                        'type'      => \Elementor\Controls_Manager::SELECT,
                        'options'               => [
                            'default'    => __( 'Default', 'xstore-core' ),
                            'advanced'        => __( 'Advanced', 'xstore-core' ),
                        ],
                        'default'   => 'default',
                        'condition' => [
                            'filter_type' => 'rating',
                        ],
                    ]
                );
    
                $repeater->add_control(
                    'price_type',
                    [
                        'label'     => esc_html__( 'Display Type', 'xstore-core' ),
                        'type'      => \Elementor\Controls_Manager::SELECT,
                        'options'               => [
                            'slider'   => esc_html__( 'Slider', 'xstore-core' ),
                            'ranges'  => esc_html__( 'Ranges', 'xstore-core' )
                        ],
                        'default'   => 'slider',
                        'condition' => [
                            'filter_type' => 'price',
                        ],
                    ]
                );
    
                $repeater->add_control(
                    'price_ranges',
                    [
                        'label' => __( 'Ranges', 'xstore-core' ),
                        'type' => \Elementor\Controls_Manager::TEXTAREA,
                        'description' => sprintf(__( 'Each range on a line, separate by the "-" symbol. Do not include the currency symbol. Example: %s', 'xstore-core' ), 'https://prnt.sc/g3WzTnI0IhXY'),
                        'rows' => 5,
                        'default' => implode("\n", array('100-200', '200-300', '300-400')),
                        'placeholder' => implode("\n", array('100-200', '200-300', '300-400')),
                        'condition' => [
                            'filter_type' => 'price',
                            'price_type' => 'ranges'
                        ],
                    ]
                );
    		
    		    $repeater->add_control(
    			    'attribute_query_type',
    			    [
    				    'label'     => esc_html__( 'Query Type', 'xstore-core' ),
    				    'type'      => \Elementor\Controls_Manager::SELECT,
    				    'default'   => 'and',
    				    'options'   => array(
    					    'or'  => esc_html__( 'OR', 'xstore-core' ),
    					    'and' => esc_html__( 'AND', 'xstore-core' ),
    				    ),
    				    'condition' => [
    					    'filter_type' => 'attributes',
    				    ],
    			    ]
    		    );
    
                $repeater->add_control(
                    'items_limit',
                    [
                        'label' 		=>	__( 'Limit', 'xstore-core' ),
                        'type' 			=>	\Elementor\Controls_Manager::NUMBER,
                        'default'	 	=>	'',
                        'min' 	=> '1',
                        'max' 	=> '',
                        'condition' => [
                            'filter_type' => ['attributes', 'rating'],
                        ],
                    ]
                );
    
                $repeater->add_control(
                    'required_field',
                    [
                        'label'        => esc_html__( 'Required field', 'xstore-core' ),
                        'type'         => \Elementor\Controls_Manager::SWITCHER,
                    ]
                );
    		
    		    $this->end_controls_section();
    		
    		    $this->start_controls_section(
    			    'section_items',
    			    [
    				    'label' => esc_html__( 'Items', 'xstore-core' ),
    			    ]
    		    );
    		
    		    //	Repeater
    		    $this->add_control(
    			    'items',
    			    [
    				    'type'        => \Elementor\Controls_Manager::REPEATER,
    				    'title_field' => '{{{ filter_title }}}',
    				    'fields'      => $repeater->get_controls(),
    				    'default'     => [
    					    [
    						    'filter_type' => 'product_cat',
    						    'filter_title' => esc_html__('Categories', 'xstore-core')
    					    ],
    					    [
    						    'filter_type' => 'rating',
    						    'filter_title' => esc_html__('Rating filter', 'xstore-core')
    					    ],
    					    [
    						    'filter_type' => 'price',
    						    'filter_title' => esc_html__('Price filter', 'xstore-core')
    					    ],
    				    ],
    			    ]
    		    );
    		
    		    $this->end_controls_section();
    		
    		    $this->start_controls_section(
    			    'button_section',
    			    [
    				    'label' => __( 'Button', 'xstore-core' ),
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'button_text',
    			    [
    				    'label' => __( 'Button Text', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::TEXT,
    				    'default' => __( 'Search', 'xstore-core' ),
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'button_selected_icon',
    			    [
    				    'label' => esc_html__( 'Icon', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::ICONS,
    				    'fa4compatibility' => 'button_icon',
    				    'skin' => 'inline',
    				    'label_block' => false,
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'button_icon_align',
    			    [
    				    'label' => esc_html__( 'Icon Position', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::SELECT,
    				    'default' => 'left',
    				    'options' => [
    					    'left' => esc_html__( 'Before', 'xstore-core' ),
    					    'right' => esc_html__( 'After', 'xstore-core' ),
    				    ],
    				    'condition' => [
    					    'button_selected_icon[value]!' => '',
    					    'button_text!' => '',
    				    ],
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'button_icon_indent',
    			    [
    				    'label' => esc_html__( 'Icon Spacing', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::SLIDER,
    				    'range' => [
    					    'px' => [
    						    'max' => 50,
    					    ],
    				    ],
    				    'selectors' => [
    					    '{{WRAPPER}} .etheme-product-filters-button .elementor-align-icon-right' => 'margin-left: {{SIZE}}{{UNIT}};',
    					    '{{WRAPPER}} .etheme-product-filters-button .elementor-align-icon-left' => 'margin-right: {{SIZE}}{{UNIT}};',
    				    ],
    				    'condition' => [
    					    'button_text!' => '',
    					    'button_selected_icon[value]!' => '',
    				    ],
    			    ]
    		    );
    		
    		    $this->end_controls_section();
    		
    		    $this->start_controls_section(
    			    'section_style_general',
    			    [
    				    'label' => esc_html__( 'General', 'xstore-core' ),
    				    'tab'   => \Elementor\Controls_Manager::TAB_STYLE,
    			    ]
    		    );
    		
    		    $this->add_responsive_control(
    			    'cols_gap',
    			    [
    				    'label' => __( 'Spacing To Button', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::SLIDER,
    				    'size_units' => [ 'px' ],
    				    'range' => [
    					    'px' => [
    						    'min' => 0,
    						    'max' => 100,
    						    'step' => 1,
    					    ],
    				    ],
    				    'selectors' => [
    					    '{{WRAPPER}}' => '--cols-gap: {{SIZE}}{{UNIT}};',
    				    ],
    			    ]
    		    );
    		
    		    $this->add_group_control(
    			    \Elementor\Group_Control_Background::get_type(),
    			    [
    				    'name'     => 'items_wrapper_background',
    				    'types'    => [ 'classic', 'gradient' ],
    				    'selector' => '{{WRAPPER}} .etheme-product-filters-items',
    				    'condition' => [
    					    'type' => 'inline'
    				    ]
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'separator_items_wrapper_background_style',
    			    [
    				    'type' => \Elementor\Controls_Manager::DIVIDER,
    				    'condition' => [
    					    'type' => 'inline'
    				    ]
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'items_wrapper_border_radius',
    			    [
    				    'label' => esc_html__( 'Border Radius', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::DIMENSIONS,
    				    'size_units' => [ 'px', '%' ],
    				    'selectors' => [
    					    '{{WRAPPER}} .etheme-product-filters-items' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
    				    ],
    				    'condition' => [
    					    'type' => 'inline'
    				    ],
    				    'default' => [
    					    'top' => 30,
    					    'left' => 30,
    					    'right' => 30,
    					    'bottom' => 30,
    					    'unit' => 'px'
    				    ]
    			    ]
    		    );
    		
    		    $this->add_group_control(
    			    \Elementor\Group_Control_Border::get_type(),
    			    [
    				    'name'      => 'items_wrapper_border',
    				    'label'     => esc_html__( 'Border', 'xstore-core' ),
    				    'selector'  => '{{WRAPPER}} .etheme-product-filters-items',
    				    'condition' => [
    					    'type' => 'inline'
    				    ],
    				    'fields_options' => [
    					    'border' => [
    						    'default' => 'solid',
    					    ],
    					    'width' => [
    						    'default' => [
    							    'top' => 1,
    							    'left' => 1,
    							    'right' => 1,
    							    'bottom' => 1
    						    ],
    					    ],
    					    'color' => [
    						    'default' => '#e1e1e1',
    					    ]
    				    ],
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'delimiter_style_heading',
    			    [
    				    'label' => __( 'Delimiter', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::HEADING,
    				    'separator' => 'before',
    				    'condition' => [
    					    'type' => 'inline'
    				    ]
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'delimiter_width',
    			    [
    				    'label' => __( 'Width', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::SLIDER,
    				    'size_units' => [ 'px' ],
    				    'range' => [
    					    'px' => [
    						    'min' => 0,
    						    'max' => 5,
    						    'step' => 1,
    					    ],
    				    ],
    				    'selectors' => [
    					    '{{WRAPPER}}' => '--delimiter-width: {{SIZE}}{{UNIT}};',
    				    ],
    				    'condition' => [
    					    'type' => 'inline'
    				    ]
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'delimiter_style',
    			    [
    				    'label' => __( 'Style', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::SELECT,
    				    'options' => [
    					    'solid' => __( 'Solid', 'xstore-core' ),
    					    'double' => __( 'Double', 'xstore-core' ),
    					    'dotted' => __( 'Dotted', 'xstore-core' ),
    					    'dashed' => __( 'Dashed', 'xstore-core' ),
    				    ],
    				    'selectors' => [
    					    '{{WRAPPER}}' => '--delimiter-style: {{VALUE}};',
    				    ],
    				    'condition' => [
    					    'type' => 'inline'
    				    ]
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'delimiter_color',
    			    [
    				    'label' => __( 'Color', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::COLOR,
    				    'selectors' => [
    					    '{{WRAPPER}}' => '--delimiter-color: {{VALUE}}',
    				    ],
    				    'condition' => [
    					    'type' => 'inline'
    				    ]
    			    ]
    		    );
    		
    		    $this->end_controls_section();
    		
    		    $this->start_controls_section(
    			    'item_style_section',
    			    [
    				    'label' => esc_html__( 'Items', 'xstore-core' ),
    				    'tab'   => \Elementor\Controls_Manager::TAB_STYLE,
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'item_spacing',
    			    [
    				    'label' => __( 'Spacing', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::SLIDER,
    				    'size_units' => [ 'px' ],
    				    'range' => [
    					    'px' => [
    						    'min' => 0,
    						    'max' => 100,
    						    'step' => 1,
    					    ],
    				    ],
    				    'selectors' => [
    					    '{{WRAPPER}}' => '--inner-cols-gap: {{SIZE}}{{UNIT}};',
    				    ],
    			    ]
    		    );
    		
    		    $this->add_group_control(
    			    \Elementor\Group_Control_Typography::get_type(),
    			    [
    				    'name' => 'item_typography',
    				    'selector' => '{{WRAPPER}} .etheme-product-filters-item-title',
    			    ]
    		    );
    		
    		    $this->start_controls_tabs( 'item_tabs' );
    		
    		    $this->start_controls_tab(
    			    'item_tab_normal',
    			    [
    				    'label' => __( 'Normal', 'xstore-core' ),
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'item_color',
    			    [
    				    'label' => esc_html__( 'Text Color', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::COLOR,
    				    'selectors' => [
    					    '{{WRAPPER}} .etheme-product-filters-item-title' => 'color: {{VALUE}};',
    				    ],
    			    ]
    		    );
    		
    		    $this->add_group_control(
    			    \Elementor\Group_Control_Background::get_type(),
    			    [
    				    'name' => 'item_background',
    				    'label' => esc_html__( 'Background', 'xstore-core' ),
    				    'types' => [ 'classic', 'gradient' ],
    				    'exclude' => [ 'image' ],
    				    'selector' => '{{WRAPPER}} .etheme-product-filters-item-title',
    			    ]
    		    );
    		
    		    $this->end_controls_tab();
    		
    		    $this->start_controls_tab(
    			    'item_tab_active',
    			    [
    				    'label' => __( 'Active', 'xstore-core' ),
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'item_color_active',
    			    [
    				    'label' => esc_html__( 'Text Color', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::COLOR,
    				    'selectors' => [
    					    '{{WRAPPER}} .opened .etheme-product-filters-item-title' => 'color: {{VALUE}};',
    				    ],
    			    ]
    		    );
    		
    		    $this->add_group_control(
    			    \Elementor\Group_Control_Background::get_type(),
    			    [
    				    'name' => 'item_background_active',
    				    'label' => esc_html__( 'Background', 'xstore-core' ),
    				    'types' => [ 'classic', 'gradient' ],
    				    'exclude' => [ 'image' ],
    				    'selector' => '{{WRAPPER}} .opened .etheme-product-filters-item-title',
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'item_border_color_active',
    			    [
    				    'label' => esc_html__( 'Border Color', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::COLOR,
    				    'condition' => [
    					    'type!' => 'inline',
    					    'item_border_border!' => '',
    				    ],
    				    'selectors' => [
    					    '{{WRAPPER}} .opened .etheme-product-filters-item-title' => 'border-color: {{VALUE}};',
    				    ],
    			    ]
    		    );
    		
    		    $this->end_controls_tab();
    		
    		    $this->end_controls_tabs();
    		
    		    $this->add_group_control(
    			    \Elementor\Group_Control_Border::get_type(),
    			    [
    				    'name' => 'item_border',
    				    'selector' => '{{WRAPPER}} .etheme-product-filters-item-title',
    				    'separator' => 'before',
    				    'condition' => [
    					    'type!' => 'inline',
    				    ],
    				    'fields_options' => [
    					    'border' => [
    						    'default' => 'solid',
    					    ],
    					    'width' => [
    						    'default' => [
    							    'top' => 1,
    							    'left' => 1,
    							    'right' => 1,
    							    'bottom' => 1
    						    ],
    					    ],
    					    'color' => [
    						    'default' => '#e1e1e1',
    					    ]
    				    ],
    			    ]
    		    );
    		
    		    $this->add_responsive_control(
    			    'item_padding',
    			    [
    				    'label' => esc_html__( 'Padding', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::DIMENSIONS,
    				    'size_units' => [ 'px', '%', 'em' ],
    				    'selectors' => [
    					    '{{WRAPPER}} .etheme-product-filters-item-title' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
    					    '{{WRAPPER}} .etheme-product-filters-item-title:not(:last-child):after' => 'top: {{TOP}}{{UNIT}}; bottom: {{BOTTOM}}{{UNIT}};',
    				    ],
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'item_border_radius',
    			    [
    				    'label' => esc_html__( 'Border Radius', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::DIMENSIONS,
    				    'size_units' => [ 'px', '%' ],
    				    'selectors' => [
    					    '{{WRAPPER}} .etheme-product-filters-item-title' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
    				    ],
    				    'condition' => [
    					    'type!' => 'inline'
    				    ],
    				    'default' => [
    					    'top' => 3,
    					    'left' => 3,
    					    'right' => 3,
    					    'bottom' => 3,
    					    'unit' => 'px'
    				    ]
    			    ]
    		    );
    		
    		    $this->end_controls_section();
    		
    		    $this->start_controls_section(
    			    'item_content_style_section',
    			    [
    				    'label' => esc_html__( 'Item Content', 'xstore-core' ),
    				    'tab'   => \Elementor\Controls_Manager::TAB_STYLE,
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'item_content_color',
    			    [
    				    'label' => esc_html__( 'Text Color', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::COLOR,
    				    'selectors' => [
    					    '{{WRAPPER}} .etheme-product-filters-item-content' => 'color: {{VALUE}};',
    				    ],
    			    ]
    		    );
    		
    		    $this->add_group_control(
    			    \Elementor\Group_Control_Background::get_type(),
    			    [
    				    'name' => 'item_content_background',
    				    'label' => esc_html__( 'Background', 'xstore-core' ),
    				    'types' => [ 'classic', 'gradient' ],
    				    'exclude' => [ 'image' ],
    				    'selector' => '{{WRAPPER}} .etheme-product-filters-item-content',
    			    ]
    		    );
    		
    		    $this->add_group_control(
    			    \Elementor\Group_Control_Border::get_type(),
    			    [
    				    'name' => 'item_content_border',
    				    'selector' => '{{WRAPPER}} .etheme-product-filters-item-content',
    				    'separator' => 'before',
    				    'fields_options' => [
    					    'border' => [
    						    'default' => 'solid',
    					    ],
    					    'width' => [
    						    'default' => [
    							    'top' => 1,
    							    'left' => 1,
    							    'right' => 1,
    							    'bottom' => 1
    						    ],
    					    ],
    					    'color' => [
    						    'default' => '#e1e1e1',
    					    ]
    				    ],
    			    ]
    		    );
    		
    		    $this->end_controls_section();
    		
    		    $this->start_controls_section(
    			    'section_button_style',
    			    [
    				    'label' => esc_html__( 'Button', 'xstore-core' ),
    				    'tab' => \Elementor\Controls_Manager::TAB_STYLE,
    			    ]
    		    );
    		
    		    $this->add_group_control(
    			    \Elementor\Group_Control_Typography::get_type(),
    			    [
    				    'name' => 'button_typography',
    				    'selector' => '{{WRAPPER}} .etheme-product-filters-button .elementor-button',
    			    ]
    		    );
    		
    		    $this->start_controls_tabs( 'tabs_button_style' );
    		
    		    $this->start_controls_tab(
    			    'tab_button_normal',
    			    [
    				    'label' => esc_html__( 'Normal', 'xstore-core' ),
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'button_text_color',
    			    [
    				    'label' => esc_html__( 'Text Color', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::COLOR,
    				    'default' => '#fff',
    				    'selectors' => [
    					    '{{WRAPPER}} .etheme-product-filters-button .elementor-button' => 'fill: {{VALUE}}; color: {{VALUE}};',
    				    ],
    			    ]
    		    );
    		
    		    $this->add_group_control(
    			    \Elementor\Group_Control_Background::get_type(),
    			    [
    				    'name' => 'button_background',
    				    'label' => esc_html__( 'Background', 'xstore-core' ),
    				    'types' => [ 'classic', 'gradient' ],
    				    'exclude' => [ 'image' ],
    				    'selector' => '{{WRAPPER}} .etheme-product-filters-button .elementor-button',
    				    'fields_options' => [
    					    'background' => [
    						    'default' => 'classic',
    					    ],
    					    'color' => [
    						    'default' => '#000000',
    					    ],
    				    ],
    			    ]
    		    );
    		
    		    $this->end_controls_tab();
    		
    		    $this->start_controls_tab(
    			    'tab_button_hover',
    			    [
    				    'label' => esc_html__( 'Hover', 'xstore-core' ),
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'button_hover_color',
    			    [
    				    'label' => esc_html__( 'Text Color', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::COLOR,
    				    'selectors' => [
    					    '{{WRAPPER}} .etheme-product-filters-button .elementor-button:hover, {{WRAPPER}} .etheme-product-filters-button .elementor-button:focus' => 'color: {{VALUE}};',
    					    '{{WRAPPER}} .etheme-product-filters-button .elementor-button:hover svg, {{WRAPPER}} .etheme-product-filters-button .elementor-button:focus svg' => 'fill: {{VALUE}};',
    				    ],
    			    ]
    		    );
    		
    		    $this->add_group_control(
    			    \Elementor\Group_Control_Background::get_type(),
    			    [
    				    'name' => 'button_background_hover',
    				    'label' => esc_html__( 'Background', 'xstore-core' ),
    				    'types' => [ 'classic', 'gradient' ],
    				    'exclude' => [ 'image' ],
    				    'selector' => '{{WRAPPER}} .etheme-product-filters-button .elementor-button:hover, {{WRAPPER}} .etheme-product-filters-button .elementor-button:focus',
    				    'fields_options' => [
    					    'background' => [
    						    'default' => 'classic',
    					    ],
    					    'color' => [
    						    'default' => '#3f3f3f'
    					    ]
    				    ],
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'button_hover_border_color',
    			    [
    				    'label' => esc_html__( 'Border Color', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::COLOR,
    				    'condition' => [
    					    'button_border_border!' => '',
    				    ],
    				    'selectors' => [
    					    '{{WRAPPER}} .etheme-product-filters-button .elementor-button:hover, {{WRAPPER}} .etheme-product-filters-button .elementor-button:focus' => 'border-color: {{VALUE}};',
    				    ],
    			    ]
    		    );
    		
    		    $this->end_controls_tab();
    		
    		    $this->end_controls_tabs();
    		
    		    $this->add_group_control(
    			    \Elementor\Group_Control_Border::get_type(),
    			    [
    				    'name' => 'button_border',
    				    'selector' => '{{WRAPPER}} .etheme-product-filters-button .elementor-button',
    				    'separator' => 'before',
    				    'fields_options' => [
    					    'border' => [
    						    'default' => 'solid',
    					    ],
    					    'width' => [
    						    'default' => [
    							    'top' => 0,
    							    'left' => 0,
    							    'right' => 0,
    							    'bottom' => 0
    						    ]
    					    ],
    				    ],
    			    ]
    		    );
    		
    		    $this->add_control(
    			    'button_border_radius',
    			    [
    				    'label' => esc_html__( 'Border Radius', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::DIMENSIONS,
    				    'size_units' => [ 'px', '%', 'em' ],
    				    'selectors' => [
    					    '{{WRAPPER}} .etheme-product-filters-button .elementor-button' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
    				    ],
    			    ]
    		    );
    		
    		    $this->add_responsive_control(
    			    'button_padding',
    			    [
    				    'label' => esc_html__( 'Padding', 'xstore-core' ),
    				    'type' => \Elementor\Controls_Manager::DIMENSIONS,
    				    'size_units' => [ 'px', 'em', '%' ],
    				    'selectors' => [
    					    '{{WRAPPER}} .etheme-product-filters-button .elementor-button' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
    				    ],
    				    'separator' => 'before',
    			    ]
    		    );
    		
    		    $this->end_controls_section();
            }
    	}
    	
    	/**
    	 * Render product filters widget output on the frontend.
    	 *
    	 * Written in PHP and used to generate the final HTML.
    	 *
    	 * @since 4.0.8
    	 * @access protected
    	 */
    	protected function render() {
    		
    		if ( !class_exists('WooCommerce') ) {
    			echo '<div class="elementor-panel-alert elementor-panel-alert-warning">'.
    			     esc_html__('Install WooCommerce Plugin to use this widget', 'xstore-core') .
    			     '</div>';
    			return;
    		}
    		
    		$settings = $this->get_settings_for_display();
            $edit_mode = Elementor::is_editor_or_preview_mode();
    		$migrated = isset( $settings['__fa4_migrated']['button_selected_icon'] );
    		$is_new = empty( $settings['button_icon'] ) && \Elementor\Icons_Manager::is_migration_allowed();
    		
    		global $wp;
    		
    		$form_action = wc_get_page_permalink( 'shop' );
    		
    		if ( get_query_var('et_is-woocommerce-archive', false) && apply_filters( 'xstore_filters_form_action_without_cat_widget', false ) ) {
    			if ( '' === get_option( 'permalink_structure' ) ) {
    				$form_action = remove_query_arg( array( 'page', 'paged', 'product-page' ), add_query_arg( $wp->query_string, '', home_url( $wp->request ) ) );
    			} else {
    				$form_action = preg_replace( '%\/page/[0-9]+%', '', home_url( trailingslashit( $wp->request ) ) );
    			}
    		}
    		
    		$this->add_render_attribute(
    			[
    				'wrapper' => [
    					'class'  => [
    						'etheme-product-filters',
    					],
    					'action' => [
    						$form_action,
    					],
    					'data-origin-action' => [
    						$form_action,
    					],
    					'method' => [
    						'GET',
    					],
                        'data-type' => [
                            $settings['type']
                        ]
    				],
    			]
    		);
    		
    		if ( get_query_var('et_is-woocommerce-archive', false) ) {
    			$this->add_render_attribute( 'wrapper', 'class', 'with-ajax' );
    		}
    		
    		?>
    		<form <?php echo $this->get_render_attribute_string( 'wrapper' ); ?>>
                <div class="etheme-product-filters-items">
                    <?php
                        foreach ( $settings['items'] as $index => $item ) :
                            
                            switch ($item['filter_type']) {
                                case 'attributes':
                                    $this->attributes_filter_template( $item );
                                    break;
                                case 'price':
                                    $this->price_filter_template( $item, $edit_mode );
                                    break;
                                case 'orderby':
                                    $this->orderby_filter_template( $item );
                                    break;
                                case 'rating':
                                    $this->rating_filter_template( $item, $form_action );
                                    break;
                                case 'on_sale': // <-- Añade este caso
                                    $this->on_sale_filter_template( $item );
                                    break;
                                default:
                                    if ( in_array($item['filter_type'], array_keys($this->product_taxonomies_to_filter())) ) {
                                        $this->taxonomy_filter_template( $item );
                                    }
                                    break;
                            }
                        
                        endforeach;
                    ?>
                </div>
                
                <?php
    
                $this->add_render_attribute( [
                    'button-wrapper' => [
                        'class' => [
                            'etheme-product-filters-button',
                        ]
                    ],
    	            'button' => [
    		            'class' => [
                            'elementor-button'
                        ]
    	            ],
    	            'button-icon-align' => [
    		            'class' => [
    			            'elementor-button-icon',
    			            'elementor-align-icon-' . $settings['button_icon_align'],
    		            ],
    	            ],
                    'content-wrapper' => [
                        'class' => 'elementor-button-content-wrapper',
                    ],
    	            'text' => [
    		            'class' => 'elementor-button-text',
    	            ],
                ] );
                // $this->add_render_attribute( 'button', 'class', 'elementor-button' );
                $this->add_render_attribute( 'button', 'role', 'button' );
                $this->add_render_attribute( 'button', 'type', 'submit' );
    
                ?>
            
                <div <?php $this->print_render_attribute_string( 'button-wrapper' ); ?>>
                    <button <?php $this->print_render_attribute_string( 'button' ); ?>>
                        <span <?php $this->print_render_attribute_string( 'content-wrapper' ); ?>>
                            <?php if ( ! empty( $settings['button_icon'] ) || ! empty( $settings['button_selected_icon']['value'] ) ) : ?>
                                <span <?php $this->print_render_attribute_string( 'button-icon-align' ); ?>>
                                <?php if ( $is_new || $migrated ) :
                                    \Elementor\Icons_Manager::render_icon( $settings['button_selected_icon'], [ 'aria-hidden' => 'true' ] );
                                else : ?>
                                    <i class="<?php echo esc_attr( $settings['button_icon'] ); ?>" aria-hidden="true"></i>
                                <?php endif; ?>
                            </span>
                            <?php endif; ?>
                            <span <?php $this->print_render_attribute_string( 'text' ); ?>>
                                <?php echo $settings['button_text'] ?? esc_html__( 'Search', 'xstore-core' ); ?>
                            </span>
                        </span>
                    </button>
                </div>
                
    		</form>
    		<?php
    		
    	}
    	
    	/**
    	 * Render Rating filter item.
    	 *
    	 * @param $settings
    	 * @param $link
    	 * @return void
    	 *
    	 * @since 4.0.8
    	 *
    	 */
    	public function rating_filter_template($settings, $link) {
    		
    		$rating_filter = array();
    		$html = '';
    		for ( $rating = 5; $rating >= 1; $rating-- ) {
    		 
    			$link_ratings = implode( ',', array_merge( $rating_filter, array( $rating ) ) );
    			$class       = in_array( $rating, $rating_filter, true ) ? 'wc-layered-nav-rating chosen' : 'wc-layered-nav-rating';
    			$link        = apply_filters( 'woocommerce_rating_filter_link', $link_ratings ? add_query_arg( 'rating_filter', $link_ratings, $link ) : remove_query_arg( 'rating_filter' ) );
    			$rating_html = wc_get_star_rating_html( $rating );
    			$count_html  = '';
    			$label = $settings['rating_type'] == 'advanced' ? ('<em>' . ($rating >= 5 ? $rating : sprintf(esc_html__('%s & Up', 'xstore-core'), $rating) ) . '</em>') : '';
    			$html .= sprintf( '<li class="%s"><a class="filter-item" href="%s" data-value="%s"><span class="star-rating">%s</span> %s %s</a></li>', esc_attr( $class ), esc_url( $link ), $rating, $rating_html, $label, $count_html ); // WPCS: XSS ok.
    		}
    
            $ratings_args = array(
                'name' => 'rating_filter',
            );
    
            if ( !!$settings['items_limit'] )
                $ratings_args['attr'] = array('data-limit="'.$settings['items_limit'].'"');
    
    	    $this->render_item_content(
                $settings,
                $html,
                $ratings_args
            );
    		
    	}
    	
    	/**
    	 * Render Price filter item.
    	 *
    	 * @param $settings
    	 * @return void
    	 *
    	 * @since 4.0.8
    	 *
    	 */
    	public function price_filter_template( $settings, $edit_mode ) {
    		$range_type = $settings['price_type'] == 'ranges';
            if ( !$range_type )
    		    $this->get_price_scripts();
    		
    		$prices = $this->get_filtered_price();
    		
    		$step = max( apply_filters( 'woocommerce_price_filter_widget_step', 10 ), 1 );
    		
    		$min = apply_filters( 'woocommerce_price_filter_widget_min_amount', floor( $prices->min_price ) );
    		$max = apply_filters( 'woocommerce_price_filter_widget_max_amount', ceil( $prices->max_price ) );
    		
    		if ( $min === $max ) {
    			return;
    		}
    		
    		if ( ( get_query_var('et_is-woocommerce-archive', false) ) && ! WC()->query->get_main_query()->post_count ) {
    			return;
    		}
    		
    		$min_price = isset( $_GET['min_price'] ) ? wc_clean( wp_unslash( $_GET['min_price'] ) ) : $min;
    		$max_price = isset( $_GET['max_price'] ) ? wc_clean( wp_unslash( $_GET['max_price'] ) ) : $max;
    
            $extra_atts = array();
    
            $html = '<div class="'.($range_type ? 'widget_price_filter' : 'price_slider_wrapper').'">';
        
                if ( $edit_mode ) {
                        $html .= __('Price filter is not available in edit mode', 'xstore-core');
                        }
                else {
                    if ( $range_type ) {
                        $price_ranges = $this->get_range_price($settings);
                        $filter_name = 'price';
                        $range_args            = array(
                            'name'        			=> $filter_name,
                            'current'     			=> array(),
                            'options'     			=> $price_ranges,
                            'multiple'    			=> 0,
                            'show_counts' 			=> 0,
                            'display_type' 			=> 'range',
                            'source' 	   			=> 'price',
                            'button_text' => esc_html__('Apply', 'xstore-core')
                        );
                        $range_args['current']['min'] = $min_price;
                        $range_args['current']['max'] = $max_price;
                        ob_start();
                        $this->render_range_price($range_args);
                        $html .= ob_get_clean();
                        $extra_atts['attr'] = array('data-limit="1"');
                    }
                    else {
                        $extra_atts['result_input'] = false;
                        $html .=
                            '<div class="etheme_price_slider" style="display:none;"></div>' .
                            '<div class="price_slider_amount" data-step="' . esc_attr($step) . '">' .
                            '<input type="text" id="min_price" name="min_price" value="' . esc_attr($min_price) . '" data-min="' . esc_attr($min) . '" placeholder="' . esc_attr__('Min price', 'xstore-core') . '" />' .
                            '<input type="text" id="max_price" name="max_price" value="' . esc_attr($max_price) . '" data-max="' . esc_attr($max) . '" placeholder="' . esc_attr__('Max price', 'xstore-core') . '" />' .
                            '<div class="price_label" style="display:none;">' .
                            esc_html__('Price:', 'xstore-core') . '<span class="from"></span> &mdash; <span class="to"></span>' .
                            '</div>' .
                            wc_query_string_form_fields(null, array('min_price', 'max_price', 'paged'), '', true) .
                            '<div class="clear"></div>' .
                            '</div>';
                    }
                }
            
            $html .= '</div>';
    		
    		$this->render_item_content($settings, $html, array_merge($extra_atts, array('html_tag' => 'div')));
    	}
    	
    	/**
    	 * Render Order By filter item.
    	 *
    	 * @param $settings
    	 * @return void
    	 *
    	 * @since 4.0.8
    	 *
    	 */
    	public function orderby_filter_template($settings) {
    		$options = apply_filters(
    			'woocommerce_catalog_orderby',
    			array(
    				'menu_order' => __( 'Default sorting', 'xstore-core' ),
    				'popularity' => __( 'Sort by popularity', 'xstore-core' ),
    				'rating'     => __( 'Sort by average rating', 'xstore-core' ),
    				'date'       => __( 'Sort by latest', 'xstore-core' ),
    				'price'      => __( 'Sort by price: low to high', 'xstore-core' ),
    				'price-desc' => __( 'Sort by price: high to low', 'xstore-core' ),
    			)
    		);
    		
    		$html = '';
    		foreach ( $options as $key => $value ) {
    			$html .=
    				'<li>' .
                        '<span class="filter-item" data-value="' . esc_attr( $key ) . '">' .
                            esc_html( $value ) .
                        '</span>' .
    				'</li>';
    		}
            
            $this->render_item_content($settings, $html, array('attr' => array('data-limit="1"')));
    	}
    	
    	/**
    	 * Render Attributes filter item.
    	 *
    	 * @param $settings
    	 * @return void
    	 *
    	 * @since 4.0.8
    	 *
    	 */
    	public function attributes_filter_template( $settings ) {
    		
    		if ( !$settings['attribute'] ) {
    		    return;
    		}
    		
    		$html_tag = 'ul';
    		
    		global $wpdb;
    		$taxonomy           = wc_attribute_taxonomy_name( $settings['attribute'] );
    		
    		// ! Set get_terms args
    		$terms = get_terms( $taxonomy );
    		
    		$origin_attr = substr( $taxonomy, 3 );
            $attribute_type = get_query_var('et_swatch_tax-'.$origin_attr, false);
            if ( !$attribute_type ) {
                $attr = $wpdb->get_row( $wpdb->prepare( "SELECT attribute_type FROM " . $wpdb->prefix . "woocommerce_attribute_taxonomies WHERE attribute_name = %s", $origin_attr ) );
                if ($attr && $attr->attribute_type) {
                    $attribute_type = $attr->attribute_type;
                    set_query_var('et_swatch_tax-' . $origin_attr, $attribute_type);
                }
            }
    		
    		$html = '';
    		
    		// if swatches
    		if ( get_query_var( 'et_is-swatches', false ) ||
                 ( get_theme_mod( 'enable_swatch', 1 ) && class_exists( 'St_Woo_Swatches_Base' ) )
    		    ) {
    			
    			$html_tag = 'div';
    			
    //			if ( function_exists('etheme_enqueue_style') )
    //                etheme_enqueue_style( "swatches-style");
                
    			$class = 'st-swatch-size-large';
    			$subtype      = '';
    			
    			$sw_shape = get_theme_mod('swatch_shape', 'default');
    			$sw_custom_shape = $sw_shape != 'default' ? $sw_shape : false;
    			
    			if ( strpos( $attribute_type, '-sq') !== false ) {
    				$et_attribute_type = str_replace( '-sq', '', $attribute_type );
    				if ( !$sw_custom_shape || $sw_custom_shape == 'square' ) {
    					$class .= ' st-swatch-shape-square';
    					$subtype      = 'subtype-square';
    				}
    				else if ( $sw_custom_shape == 'circle' ) {
    					$class .= ' st-swatch-shape-circle';
    				}
    			} else {
    				$et_attribute_type = $attribute_type;
    				if ( !$sw_custom_shape || $sw_custom_shape == 'circle' ) {
    					$class .= ' st-swatch-shape-circle';
    				}
    			}
    			
    			$html .= '<ul class="st-swatch-preview st-color-swatch '. esc_attr( $class ). '">';
    			
    			    foreach( $terms as $taxonomy ) {
                    if ( !is_object($taxonomy) ) continue;
    				$metadata    = get_term_meta( $taxonomy->term_id, '', true );
    				$data_tooltip = $taxonomy->name;
    				$data_slug = $taxonomy->slug;
    				$link_class = 'filter-item';
    				$validator_hidden_text = '<span class="screen-reader-text hidden">'.$data_tooltip.'</span>';
    				$li_class = '';
    				// ! Generate html
    				switch ( $et_attribute_type ) {
    					case 'st-color-swatch':
    						$value = ( isset( $metadata['st-color-swatch'] ) && isset( $metadata['st-color-swatch'][0] ) ) ? $metadata['st-color-swatch'][0] : '#fff';
    						$html .= '<li class="type-color ' . $subtype . $li_class . '"  data-tooltip="'.$data_tooltip.'">
                            <a class="'.$link_class.'" data-value="'.$data_slug.'">
                                <span class="st-custom-attribute" style="'. esc_attr( $this->generate_gradient_color_css($value) ) .'">
                                    '.$validator_hidden_text.'
                                </span>
                            </a></li>';
    						break;
    					
    					case 'st-image-swatch':
    						$value = ( isset( $metadata['st-image-swatch'] ) && isset( $metadata['st-image-swatch'][0] ) ) ? $metadata['st-image-swatch'][0] : false;
    						$image = ( $value ) ? wp_get_attachment_image( $value, apply_filters('sten_wc_filter_image_swatch_size', 'thumbnail') ) : wc_placeholder_img();
    						$html .=
                            '<li class="type-image ' . $subtype . $li_class . '"  data-tooltip="'.$data_tooltip.'">
                                <a class="'.$link_class.'" data-value="'.$data_slug.'">
                                    <span class="st-custom-attribute">'
                                         . $image . $validator_hidden_text.
                                     '</span>
                                </a>
                            </li>';
    						break;
    					
    					case 'st-label-swatch':
    						$value = ( isset( $metadata['st-label-swatch'] ) && $metadata['st-label-swatch'][0] ) ? $metadata['st-label-swatch'][0] : false;
    						
    						if ( ! $value ) {
    							$value = $taxonomy->name;
    						}
    						
    						$html .= '<li class="type-label ' . $subtype . $li_class . '"><a class="'.$link_class.'" data-value="'.$data_slug.'"><span class="st-custom-attribute">' . $value . '</span></a></li>';
    						break;
    					
    					default:
    						$html .= '<li class="type-select ' . $li_class . '"><a class="'.$link_class.'" data-value="'.$data_slug.'"><span class="st-custom-attribute">' . $taxonomy->name . '</span></a></li>';
    						break;
    				}
                }
    			    
    			$html .= '</ul>';
    			
            }
    		
    		else {
    			foreach( $terms as $taxonomy ) {
                    $html .=
                        '<li>'.
                            '<span class="filter-item" data-value="'.$taxonomy->slug.'">'.
                                $taxonomy->name .
                            '</span>'.
                        '</li>';
    			}
    		}
    
            $attr_args = array(
                'name' => 'filter_'.$settings['attribute'],
                'html_tag' => $html_tag,
                'query_type_name' => 'query_type_'.$settings['attribute'],
                'query_type_value' => $settings['attribute_query_type'],
            );
    
            if ( !!$settings['items_limit'] )
                $attr_args['attr'] = array('data-limit="'.$settings['items_limit'].'"');
    		
    		$this->render_item_content(
                $settings,
                $html,
                $attr_args
            );
    	}
    	
    	/**
    	 * Render Taxonomy filter item.
    	 *
    	 * @param $settings
    	 * @return void
    	 *
    	 * @since 4.0.8
    	 *
    	 */
    	public function taxonomy_filter_template( $settings ) {
    		global $wp_query;
    		
    		$list_args = [
    			'taxonomy'           => $settings['filter_type'],
    			'hide_empty'         => !!$settings[$settings['filter_type'].'_hide_empty'],
    			'title_li'           => false,
                'show_count' => !!$settings[$settings['filter_type'].'_show_count'],
    			'use_desc_for_title' => false,
    			'echo'               => false,
    		];
    		
    		switch ($settings['filter_type']) {
                case 'product_tag':
                    $list_args['show_option_none'] = __('No product tags', 'xstore-core');
                    break;
    			case 'brand':
    				$list_args['show_option_none'] = __('No brands', 'xstore-core');
    				break;
                default:
                    break;
    		}
    		
    		if ( isset($settings[$settings['filter_type'].'_hierarchical']) ) {
    		    $list_args['hierarchical'] = !!$settings[$settings['filter_type'].'_hierarchical'];
    		}
    		
    		$cat_ancestors = [];
    		
    		$list_args['current_category_ancestors'] = $cat_ancestors;
    		
    		$extra = array(
    		        'attr' => array(
                        'data-limit="1"'
                    )
            );
    		
    		// product_cat || product_tag
    		if ( get_query_var( $settings['filter_type'] ) ) {
    		    $term = get_term_by('slug', get_query_var( $settings['filter_type'] ), $settings['filter_type']);
    			$extra['is_active'] = true;
    			$extra['active']['value'] = get_query_var( $settings['filter_type'] );
    			$extra['active']['label'] = $term->name;
    		}
    		
    		add_filter( 'category_list_link_attributes', array($this, 'filter_wp_list_categories'), 10, 2 );
    		
            $this->render_item_content($settings,  wp_list_categories( $list_args ), $extra);
    		
    		remove_filter( 'category_list_link_attributes', array($this, 'filter_wp_list_categories'), 10, 2 );
    		
    	}
    	
    	/**
    	 * Get Price scripts.
    	 *
    	 * @param $settings
    	 * @param $link
    	 * @return void
    	 *
    	 * @since 4.0.8
    	 *
    	 */
    	protected function get_price_scripts() {
    		wp_localize_script(
    			'wc-price-slider',
    			'woocommerce_price_slider_params',
    			[
    				'currency_format_num_decimals' => 0,
    				'currency_format_symbol'       => get_woocommerce_currency_symbol(),
    				'currency_format_decimal_sep'  => esc_attr( wc_get_price_decimal_separator() ),
    				'currency_format_thousand_sep' => esc_attr( wc_get_price_thousand_separator() ),
    				'currency_format'              => esc_attr( str_replace( array( '%1$s', '%2$s' ), array( '%s', '%v' ), get_woocommerce_price_format() ) ),
    			]
    		);
    		wp_enqueue_script( 'jquery-ui-slider' );
    		wp_enqueue_script( 'wc-jquery-ui-touchpunch' );
    		wp_enqueue_script( 'accounting' );
    		wp_enqueue_script( 'wc-price-slider' );
    	}
    	
    	/**
    	 * Get filtered min price for current products.
    	 *
    	 * @return int
         *
    	 * @since 4.0.8
         *
    	 */
    	protected function get_filtered_price() {
    		global $wpdb;
    		$tax_query  = array();
    		$meta_query = array();
    		if ( ! is_post_type_archive( 'product' ) && ! empty( $args['taxonomy'] ) && ! empty( $args['term'] ) ) {
    			$tax_query[] = array(
    				'taxonomy' => $args['taxonomy'],
    				'terms'    => array( $args['term'] ),
    				'field'    => 'slug',
    			);
    		}
    		
    		$meta_query = new \WP_Meta_Query( $meta_query );
    		$tax_query  = new \WP_Tax_Query( $tax_query );
    		$meta_query_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
    		$tax_query_sql  = $tax_query->get_sql( $wpdb->posts, 'ID' );
    		$sql  = "SELECT min( FLOOR( price_meta.meta_value ) ) as min_price, max( CEILING( price_meta.meta_value ) ) as max_price FROM {$wpdb->posts} ";
    		$sql .= " LEFT JOIN {$wpdb->postmeta} as price_meta ON {$wpdb->posts}.ID = price_meta.post_id " . $tax_query_sql['join'] . $meta_query_sql['join'];
    		$sql .= " 	WHERE {$wpdb->posts}.post_type IN ('" . implode( "','", array_map( 'esc_sql', apply_filters( 'woocommerce_price_filter_post_type', array( 'product' ) ) ) ) . "')
    			AND {$wpdb->posts}.post_status = 'publish'
    			AND price_meta.meta_key IN ('" . implode( "','", array_map( 'esc_sql', apply_filters( 'woocommerce_price_filter_meta_keys', array( '_price' ) ) ) ) . "')
    			AND price_meta.meta_value > '' ";
    		$sql .= $tax_query_sql['where'] . $meta_query_sql['where'];
    		
    		$sql = apply_filters( 'woocommerce_price_filter_sql', $sql, $meta_query_sql, $tax_query_sql );
    		return $wpdb->get_row( $sql ); // WPCS: unprepared SQL ok.
    	}
    	
    	/**
    	 * Add class for wp list categories via filter.
    	 *
    	 * @param $atts
    	 * @param $category
    	 * @return mixed
    	 *
    	 * @since 4.0.8
    	 *
    	 */
    	public function filter_wp_list_categories( $atts, $category ) {
            if ( $category->slug )
    		    $atts['data-value'] = $category->slug;
          
    		$atts['data-name'] = $category->name;
    		
    		$atts['class'] = isset($atts['class']) ? isset($atts['class']). ' filter-item' : 'filter-item';
    		
    		return $atts;
    	}
    	
    	/**
    	 * Render Item Content.
    	 *
    	 * @param       $settings
    	 * @param       $html
    	 * @param array $extra
    	 * @return void
    	 *
    	 * @since 4.0.8
    	 *
    	 */
    	public function render_item_content($settings, $html, $extra = array()) {
    	    $extra = shortcode_atts(array(
                'name' => $settings['filter_type'],
                'required' => $settings['required_field'],
                'html_tag' => 'ul',
                'result_input' => true,
                'attr' => array(),
                'query_type_name' => false,
                'query_type_value' => false,
                'is_active' => false,
                'active' => array(
                    'value' => '',
                    'label' => ''
                )
            ), $extra);
    
    	    if ( $extra['required'] )
                $extra['attr'][] = 'data-required="yes"';
    	    
    	    ?>
            <div class="etheme-product-filters-item" <?php echo implode(' ', $extra['attr']); ?>>
                <?php if ( $extra['result_input']) :
                    // tweak for setting empty input name for price to prevent sending param in url but keep input for JS code
                ?>
                    <input type="hidden" class="result-input" name="<?php echo $settings['filter_type'] != 'price' ? $extra['name'] : ''; ?>" value="<?php echo $extra['is_active'] ? $extra['active']['value'] : ''; ?>">
                <?php endif; ?>
                <?php if ( $extra['query_type_name'] && $extra['query_type_value'] ) : ?>
                    <input type="hidden" name="<?php echo $extra['query_type_name']; ?>" value="<?php echo $extra['query_type_value']; ?>">
                <?php endif; ?>
                <div class="etheme-product-filters-item-title">
                        <span class="title-text">
                            <?php echo esc_html( $settings['filter_title'] ) . ($extra['required'] ? ' <span class="required">*</span> ' : ''); ?>
                        </span>
        
                    <span class="etheme-product-filters-quick-results">
                        <?php if ( $extra['is_active'] ) : ?>
                            <span data-q-value="<?php echo esc_attr($extra['active']['value']); ?>">
                                <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width=".75em" height=".75em" viewBox="0 0 24 24">
                                    <path d="M13.056 12l10.728-10.704c0.144-0.144 0.216-0.336 0.216-0.552 0-0.192-0.072-0.384-0.216-0.528-0.144-0.12-0.336-0.216-0.528-0.216 0 0 0 0 0 0-0.192 0-0.408 0.072-0.528 0.216l-10.728 10.728-10.704-10.728c-0.288-0.288-0.768-0.288-1.056 0-0.168 0.144-0.24 0.336-0.24 0.528 0 0.216 0.072 0.408 0.216 0.552l10.728 10.704-10.728 10.704c-0.144 0.144-0.216 0.336-0.216 0.552s0.072 0.384 0.216 0.528c0.288 0.288 0.768 0.288 1.056 0l10.728-10.728 10.704 10.704c0.144 0.144 0.336 0.216 0.528 0.216s0.384-0.072 0.528-0.216c0.144-0.144 0.216-0.336 0.216-0.528s-0.072-0.384-0.216-0.528l-10.704-10.704z"></path>
                                </svg><?php echo $extra['active']['label'] ?></span>
                        <?php endif; ?>
                        <?php if ( $settings['filter_type'] == 'price' && $settings['price_type'] != 'ranges' ) { ?>
                            <span style="display:none;" data-q-value="price">
                                <span class="from"></span> &mdash; <span class="to"></span>
                            </span>
                        <?php } ?>
                    </span>
                </div>
    
                <<?php echo $extra['html_tag']; ?> class="etheme-product-filters-item-content" style="display: none">
                    <?php echo $html; ?>
                </<?php echo $extra['html_tag']; ?>>
            </div>
            <?php
    	}
    	
    	/**
    	 * Return filtered product taxonomies for filters.
    	 *
    	 * @since 4.0.8
    	 *
    	 * @return mixed
    	 */
    	public function product_taxonomies_to_filter() {
    	    return apply_filters('etheme_product_filters_taxonomies', array(
    		    'product_cat' => esc_html__('Categories', 'xstore-core'),
    		    'product_tag' => esc_html__('Product tag', 'xstore-core'),
    	    ) );
    	}
    	
    	/**
    	 * Get product attributes.
    	 *
    	 * @since 4.0.8
    	 * @access public
    	 *
    	 * @return array Widget categories.
    	 */
    	public function get_attributes() {
    		$output = [
    			'' => esc_html__( 'Select', 'xstore-core' ),
    		];
    		
            $taxonomies = wc_get_attribute_taxonomies();
            
            if ( $taxonomies ) {
                foreach ( $taxonomies as $tax ) {
                    $output[ $tax->attribute_name ] = $tax->attribute_name;
                }
            }
    		
    		return $output;
    	}
    
        protected function get_range_price($filter) {
            $options = array();
            // Use the default price slider widget.
            if ( empty( $filter['price_ranges'] ) ) {
                return $options;
            }
    
            $ranges = explode( "\n", $filter['price_ranges'] );
    
            foreach ( $ranges as $range ) {
                $range       = trim( $range );
                $prices      = explode( '-', $range );
                $price_range = array( 'min' => '', 'max' => '' );
                $name        = array();
    
                if ( count( $prices ) > 1 ) {
                    $price_range['min'] = preg_match( '/\d+\.?\d+/', current( $prices ), $match ) ? floatval( $match[0] ) : 0;
                    $price_range['max'] = preg_match( '/\d+\.?\d+/', end( $prices ), $match ) ? floatval( $match[0] ) : 0;
                    reset( $prices );
                    $name['min'] = preg_replace( '/\d+\.?\d+/', '<span class="price">' . wc_price( $price_range['min'] ) . '</span>', current( $prices ) );
                    $name['max'] = preg_replace( '/\d+\.?\d+/', '<span class="price">' . wc_price( $price_range['max'] ) . '</span>', end( $prices ) );
                } elseif ( substr( $range, 0, 1 ) === '<' ) {
                    $price_range['max'] = preg_match( '/\d+\.?\d+/', end( $prices ), $match ) ? floatval( $match[0] ) : 0;
                    $name['max'] = preg_replace( '/\d+\.?\d+/', '<span class="price">' . wc_price( $price_range['max'] ) . '</span>', ltrim( end( $prices ), '< ' ) );
                } else {
                    $price_range['min'] = preg_match( '/\d+\.?\d+/', current( $prices ), $match ) ? floatval( $match[0] ) : 0;
                    $name['min'] = preg_replace( '/\d+\.?\d+/', '<span class="price">' . wc_price( $price_range['min'] ) . '</span>', current( $prices ) );
                }
    
                $options[] = array(
                    'name'  => implode( ' - ', $name ),
                    'count' => 0,
                    'range' => $price_range,
                    'level' => 0,
                );
            }
    
            return $options;
        }
    
        public function render_range_price($args) {
            $args = wp_parse_args( $args, array(
                'name'        => '',
                'current'     => array(),
                'options'     => array(),
                'attribute'   => '',
                'multiple'    => false,
                'show_counts' => false,
            ) );
    
            if ( empty( $args['options'] ) ) {
                return;
            }
    
            $current_page_url = home_url();
            $base_link          = remove_query_arg(array('min_price', 'max_price'), $current_page_url);
            echo '<ul class="prices-list">';
            foreach ( $args['options'] as $option ) {
                printf(
                    '<li class="price-list-item %s"><a class="filter-item" href="%s" data-value="%s">%s</a>%s</li>',
                    $args['current']['min'] == $option['range']['min'] && $args['current']['max'] == $option['range']['max'] ? 'chosen' : '',
                    add_query_arg(array('min_price' => $option['range']['min'], 'max_price' => $option['range']['max']), $base_link),
                    esc_attr( implode('-', $option['range']) ),
                    $option['name'],
                    $args['show_counts'] ? '<span class="products-filter__count counter">' . $option['count'] . '</span>' : ''
                );
            }
            echo '</ul>';
    
            echo '<div class="price-filter-box">';
    
            echo '<span class="filter-item filter-item-ghost hidden" data-value="">'.esc_html__('Custom price value', 'xstore-core').'</span>';
    
            printf(
                '<input type="number" name="min_%s" min="0" value="%s" placeholder="%s">',
                esc_attr( $args['name'] ),
                esc_attr( $args['current']['min'] ),
                esc_html__( 'Min', 'xstore-core' )
            );
    
            echo '<span class="line"></span>';
    
            printf(
                '<input type="number" name="max_%s" min="0" value="%s" placeholder="%s">',
                esc_attr( $args['name'] ),
                esc_attr( $args['current']['max'] ),
                esc_html__( 'Max', 'xstore-core' )
            );
    
            echo '<button type="submit" value="' . esc_attr__( 'Apply', 'xstore-core' ) . '" data-base-url="'.$base_link.'" class="btn btn-black medium">' . $args['button_text'] . '</button>';
    
            echo '</div>';
        }
    
    	/**
    	 * Generate color style
    	 */
    	public function generate_gradient_color_css($color) {
    		$style = '';
    		if (is_serialized($color)){
    			$color = unserialize($color);
                $gradient_direction = get_theme_mod('swatch_multicolor_design', 'right');
                if ( in_array($gradient_direction, array('diagonal_1', 'diagonal_2'))) {
                    $gradient_direction = str_replace(array('diagonal_1', 'diagonal_2'), array('bottom right', 'bottom left'), $gradient_direction);
                }
                $style .= 'background: linear-gradient( to ';
                $style .= $gradient_direction . ',';
    			$percent = 100/count($color);
    
                foreach($color as $color_key => $color_value){
                    $style .= $color_value . ' ' . $percent .'% '. ( $percent+$percent*$color_key ) . '%';
                    if ($color_key != count($color)-1){
                        $style .= ',';
                    }
                }
    
    			$style .= ');';
    		} else {
    			$style .= 'background-color:' . $color . ';';
    		}
    		return $style;
    	}
    
        public function on_sale_filter_template( $settings ) {
        $current = isset($_GET['on_sale']) ? $_GET['on_sale'] : '';
        $html = '<ul>';
        $html .= '<li><label><input type="checkbox" name="on_sale" value="1" '.checked($current, '1', false).' /> ' . esc_html__('Mostrar solo productos en oferta', 'xstore-core') . '</label></li>';
        $html .= '</ul>';
    
        $this->render_item_content(
            $settings,
            $html,
            array(
                'name' => 'on_sale',
                'html_tag' => 'ul',
                'result_input' => false,
            )
        );
    }
    }
    add_action('pre_get_posts', function($query){
        if ( !is_admin() && $query->is_main_query() && isset($_GET['on_sale']) && $_GET['on_sale'] == '1' ) {
            $query->set('meta_query', array(
                array(
                    'key' => '_sale_price',
                    'value' => 0,
                    'compare' => '>',
                    'type' => 'NUMERIC'
                )
            ));
        }
    });
    
    Files is visible for topic creator and
    support staff only.
    Avatar: Justin
    Luca Rossi
    Support staff
    August 18, 2025 at 06:09

    Dear @Juan Alberto Montoya,

    We hope this message finds you well.

    Could you kindly let us know which lines of code you modified or added for the custom implementation? Our development team would like to review your changes and consider including them in future updates.

    Thank you for your contribution and support.

    Best regards,
    The 8Theme Team

    Avatar: Juan Alberto Montoya
    Juan Alberto Montoya
    Participant
    August 18, 2025 at 10:50

    Yeah sure, thanks for reply! i drop below the modified lines of code in comparison with the original code

    Files is visible for topic creator and
    support staff only.
    Avatar: Justin
    Luca Rossi
    Support staff
    August 20, 2025 at 06:31

    Dear Juan Alberto Montoya,

    Thank you once again for your valuable input.

    Our development team is currently reviewing your suggestion and will consider incorporating it into future updates.

    Best regards,
    The 8Theme Team

  • Viewing 6 results - 1 through 6 (of 6 total)

The issue related to '‘Feature Request: Add “On Sale” Filter Option to Product Filters Widget’' has been successfully resolved, and the topic is now closed for further responses

We're using our own and third-party cookies to improve your experience and our website. Keep on browsing to accept our cookie policy.