DeTECHtive

Elementor, ACF, Checkbox to Icon List part #1

My Custom Icon list widget for Elementor and ACF

While running through some practice exercises anticipating one of my customer’s needs I had an issue with a clean, easy way to add icons to variables stored in ACF Pro. After much searching, I found no quick way to achieve it so I give you Custom Icon List Widget… May it one day have a snappier name

The Problem

Imagine you have a few rental properties and you are looking to list them, you create a website and a template and all seems lovely, now you come to list the facilities. using ACF we create the checkboxes for our rental property as below.

Setup your checkbox in ACF Pro

Now when you come to display this on the front end your options are fairly limited in how that is presented and you have to use things like ACF Field, ACF Repeater but they do not offer the ability to replace with icons as easily as this custom widget will. Below is an example of the output from ACF Field

Front end display for ACF Field, no icon options ugly look

Solution

My proposed solution is:

Create a non-invasive elementor widget using the Code Snippets plugin, importing ACF Data and running that through an algorithm that, if set, matches the text with an icon and, if not set, matches the text with a default icon.

Requirements

I will assume that you know what you are doing with the following plugins which are required for this process:

Code Snippets – This is needed for adding the PHP code from the admin panel, this can be done using an alternate method with FTP and making your own plugin but Code Snippets makes it faster and easier for you.

ACF Pro – goes without saying you need ACF Pro for the custom fields

Elementor Pro – I’m not spoon-feeding you!

Steps

  1. Create ACF field and attach it to your desired post (not in guide)
  2. Install, Active and select Code Snippets in the Admin Panel
  3. Click Add New
  4. Enter a Title such as “Checkbox to Icon List”
  5. Paste in the code below
  6. Change line 
    “$acf_values = get_field( ‘facilities2’ );”
    to 
    $acf_values = get_field( ‘yourACFvariablename’ );
  7. Click “save changes” in the top right
  8. Click “activate” in the top right
  9. Now edit a page(I suggest using a template)
  10. Add the CSS to custom CSS on the page(guide below)
  11. Place in your widget 

The Code

				
					<?php
add_action( 'elementor/widgets/widgets_registered', function( $widgets_manager ) {

    class Custom_Icon_List_Widget extends \Elementor\Widget_Base {

        public function get_name() {
            return 'custom_icon_list_widget';
        }

        public function get_title() {
            return __( 'Custom Icon List Widget', 'your-text-domain' );
        }

        public function get_icon() {
            return 'fa fa-list';
        }

        public function get_categories() {
            return [ 'general' ];
        }

        protected function _register_controls() {
            $this->start_controls_section(
                'content_section',
                [
                    'label' => __( 'Variables', 'your-text-domain' ),
                    'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
                ]
            );

            // Define the list items control
            $this->add_control(
                'list_items',
                [
                    'label' => __( 'List Items', 'your-text-domain' ),
                    'type' => \Elementor\Controls_Manager::REPEATER,
                    'fields' => [
                        [
                            'name' => 'item_title',
                            'label' => __( 'Search String', 'your-text-domain' ),
                            'type' => \Elementor\Controls_Manager::TEXT,
							'default' => esc_html__( 'List Title' , 'textdomain' ),
                        ],
                        [
                            'name' => 'icon',
                            'label' => __( 'Replace Icon', 'your-text-domain' ),
                            'type' => \Elementor\Controls_Manager::ICONS,
                            'default' => [
                                'value' => 'fas fa-star',
                                'library' => 'fa-solid',
                            ],
                        ],
                    ],
                    'title_field' => '{{{item_title}}}',
                ]
            );

            // Add a default icon control
            $this->add_control(
                'default_icon',
                [
                    'label' => __( 'Default Icon', 'your-text-domain' ),
                    'type' => \Elementor\Controls_Manager::ICONS,
                    'default' => [
                        'value' => 'fas fa-star',
                        'library' => 'fa-solid',
                    ],
                ]
            );

            $this->end_controls_section();
        }

        protected function render() {
            if ( ! function_exists( 'get_field' ) ) {
                return;
            }

            $settings = $this->get_settings_for_display();
            $acf_values = get_field( 'facilities2' );

            if ( is_array( $acf_values ) && !empty( $acf_values ) && is_array( $settings['list_items'] ) && !empty( $settings['list_items'] ) ) {

                // Convert list items into an associative array for easier lookup
                $list_items_assoc = [];
                foreach ( $settings['list_items'] as $item ) {
                    $list_items_assoc[$item['item_title']] = $item['icon'];
                }

                // Render the updated list
                echo '<ul class="custom-icon-list">';
                foreach ( $acf_values as $acf_value ) {
                    if ( isset( $list_items_assoc[$acf_value] ) ) {
                        $icon = $list_items_assoc[$acf_value];
                    } else {
                        $icon = $settings['default_icon'];  // Use the default icon if no matching item is found
                    }
                    ob_start();
                    \Elementor\Icons_Manager::render_icon( $icon, [ 'aria-hidden' => 'true' ] );
                    $icon_html = ob_get_clean();
                    ?>
                    <li><span class="icon-wrapper"><?php echo $icon_html; ?> </span> <?php echo esc_html( $acf_value ); ?></li>
                    <?php
                }
                echo '</ul>';
            }
        }
    }

    $widgets_manager->register_widget_type( new Custom_Icon_List_Widget() );
} );
?>
				
			

The Code (Pt.2)

because of how HTML UL work you need to use CSS to remove bulletpoints and lineup icons with text. In elementor edit your template or page > settings > advanced > custom CSS >

				
					.custom-icon-list {
    list-style-type: none; /* Removes bullet points */
    padding: 0; /* Removes default padding */
    margin: 0; /* Removes default margin */
}

.custom-icon-list li {
    margin-bottom: 10px; /* Adds space below each list item */
    display: flex; /* Aligns icon and text horizontally */
    align-items: center; /* Vertically centers icon and text */
    flex-wrap: nowrap; /* Prevents wrapping within list items */
}

.custom-icon-list .icon-wrapper {
    margin-right: 10px; /* Adds space between icon and text */
    display: inline-block; /* Ensures icon wrapper behaves as an inline element */
}
				
			

What it all does

This is the primary visual controls you see inside elementor, We start by creating the sections and titles, then add the list of items and the defaults associated with them.

				
					        protected function _register_controls() {
            $this->start_controls_section(
                'content_section',
                [
                    'label' => __( 'Variables', 'your-text-domain' ),
                    'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
                ]
            );

            // Define the list items control
            $this->add_control(
                'list_items',
                [
                    'label' => __( 'List Items', 'your-text-domain' ),
                    'type' => \Elementor\Controls_Manager::REPEATER,
                    'fields' => [
                        [
                            'name' => 'item_title',
                            'label' => __( 'Search String', 'your-text-domain' ),
                            'type' => \Elementor\Controls_Manager::TEXT,
							'default' => esc_html__( 'List Title' , 'textdomain' ),
                        ],
                        [
                            'name' => 'icon',
                            'label' => __( 'Replace Icon', 'your-text-domain' ),
                            'type' => \Elementor\Controls_Manager::ICONS,
                            'default' => [
                                'value' => 'fas fa-star',
                                'library' => 'fa-solid',
                            ],
                        ],
                    ],
                    'title_field' => '{{{item_title}}}',
                ]
            );

            // Add a default icon control
            $this->add_control(
                'default_icon',
                [
                    'label' => __( 'Default Icon', 'your-text-domain' ),
                    'type' => \Elementor\Controls_Manager::ICONS,
                    'default' => [
                        'value' => 'fas fa-star',
                        'library' => 'fa-solid',
                    ],
                ]
            );

            $this->end_controls_section();
        }
				
			

The Rendering is where the heavy lifting is done. This function renders the UX and loops through the data, and applies the algorithm.

				
					        protected function render() {
            if ( ! function_exists( 'get_field' ) ) {
                return;
            }

            $settings = $this->get_settings_for_display();
            $acf_values = get_field( 'facilities2' );

            if ( is_array( $acf_values ) && !empty( $acf_values ) && is_array( $settings['list_items'] ) && !empty( $settings['list_items'] ) ) {

                // Convert list items into an associative array for easier lookup
                $list_items_assoc = [];
                foreach ( $settings['list_items'] as $item ) {
                    $list_items_assoc[$item['item_title']] = $item['icon'];
                }

                // Render the updated list
                echo '<ul class="custom-icon-list">';
                foreach ( $acf_values as $acf_value ) {
                    if ( isset( $list_items_assoc[$acf_value] ) ) {
                        $icon = $list_items_assoc[$acf_value];
                    } else {
                        $icon = $settings['default_icon'];  // Use the default icon if no matching item is found
                    }
                    ob_start();
                    \Elementor\Icons_Manager::render_icon( $icon, [ 'aria-hidden' => 'true' ] );
                    $icon_html = ob_get_clean();
                    ?>
                    <li><span class="icon-wrapper"><?php echo $icon_html; ?> </span> <?php echo esc_html( $acf_value ); ?></li>
                    <?php
                }
                echo '</ul>';
            }
        }
				
			

Lets go through in a bit more detail

				
					            $settings = $this->get_settings_for_display();
            $acf_values = get_field( 'facilities2' );
				
			

This pulls the ACF Field from the SQL database

				
					            if ( is_array( $acf_values ) && !empty( $acf_values ) && is_array( $settings['list_items'] ) && !empty( $settings['list_items'] ) ) {

				
			

Next we make sure that our validations are correct (data is arrays for loops and is not NULL)

				
					  $list_items_assoc = [];
                foreach ( $settings['list_items'] as $item ) {
                    $list_items_assoc[$item['item_title']] = $item['icon'];
                }
				
			

Prepare our data into an array ready for usage

				
					                echo '<ul class="custom-icon-list">';
                foreach ( $acf_values as $acf_value ) {
                    if ( isset( $list_items_assoc[$acf_value] ) ) {
                        $icon = $list_items_assoc[$acf_value];
                    } else {
                        $icon = $settings['default_icon'];  // Use the default icon if no matching item is found
                    }
                    ob_start();
                    \Elementor\Icons_Manager::render_icon( $icon, [ 'aria-hidden' => 'true' ] );
                    $icon_html = ob_get_clean();
                    ?>
                    <li><span class="icon-wrapper"><?php echo $icon_html; ?> </span> <?php echo esc_html( $acf_value ); ?></li>
                    <?php
                }
                echo '</ul>';
            }
				
			

Now output some HTML, it creates an Unordered list, then checks if a word has an associated Icon, if it does have an Icon it displays it, if it does not have an icon it uses a default icon set in the UI.

Because $icon_html is already rendered as an output it must be created as an object and cleaned (line 8-10) otherwise you will get a 1 next to your array output.

Next on my agenda will be adding the ability to enter which ACF field you select from and looking at sorting these into columns or a table to help with display.

####
####
####