View file File name : MenuIcon.php Content :<?php /** * Custom Component class for Header Footer Grid. * * Name: Header Footer Grid * Author: Bogdan Preda <bogdan.preda@themeisle.com> * * @version 1.0.0 * @package HFG */ namespace HFG\Core\Components; use HFG\Core\Script_Register; use HFG\Core\Settings\Manager as SettingsManager; use HFG\Main; use Neve\Core\Dynamic_Css; use Neve\Core\Settings\Config; use Neve\Core\Settings\Mods; use Neve\Core\Styles\Dynamic_Selector; /** * Class MenuIcon * * @package HFG\Core\Components */ class MenuIcon extends Abstract_Component { const COMPONENT_ID = 'header_menu_icon'; const SIDEBAR_TOGGLE = 'sidebar'; const TEXT_ID = 'menu_label'; const BUTTON_APPEARANCE = 'button_appearance'; const COMPONENT_SLUG = 'nav-icon'; const QUICK_LINKS_ID = 'quick-links'; const LABEL_MARGIN_ID = 'label_margin'; const MENU_ICON = 'menu_icon'; const SIZE_ID = 'icon_size'; const MENU_SVG = 'svg_menu_icon'; /** * Padding settings default values. * * @var array */ protected $default_padding_value = array( 'mobile' => array( 'top' => 10, 'right' => 15, 'bottom' => 10, 'left' => 15, ), 'tablet' => array( 'top' => 10, 'right' => 15, 'bottom' => 10, 'left' => 15, ), 'desktop' => array( 'top' => 10, 'right' => 15, 'bottom' => 10, 'left' => 15, ), 'mobile-unit' => 'px', 'tablet-unit' => 'px', 'desktop-unit' => 'px', ); /** * Label margin settings default values. * * @var array */ protected $default_label_margin_value = array( 'mobile' => array( 'top' => 0, 'right' => 5, 'bottom' => 0, 'left' => 0, ), 'tablet' => array( 'top' => 0, 'right' => 5, 'bottom' => 0, 'left' => 0, ), 'desktop' => array( 'top' => 0, 'right' => 5, 'bottom' => 0, 'left' => 0, ), 'mobile-unit' => 'px', 'tablet-unit' => 'px', 'desktop-unit' => 'px', ); /** * Close button target CSS selector. * * @var string */ private $close_button = '.header-menu-sidebar .close-sidebar-panel .navbar-toggle'; /** * MenuIcon constructor. * * @since 1.0.0 * @access public */ public function init() { $this->set_property( 'label', __( 'Menu Icon', 'neve' ) ); $this->set_property( 'id', $this->get_class_const( 'COMPONENT_SLUG' ) ); $this->set_property( 'component_slug', self::COMPONENT_SLUG ); $this->set_property( 'width', 1 ); $this->set_property( 'icon', 'menu' ); $this->set_property( 'section', self::COMPONENT_ID ); $this->set_property( 'default_selector', '.builder-item--' . $this->get_id() . ' .navbar-toggle' ); add_action( 'wp_enqueue_scripts', [ $this, 'load_scripts' ] ); add_filter( 'neve_menu_icon_classes', array( $this, 'add_menu_icon_classes' ), 10, 2 ); } /** * Filter classes for menu icon. * * @param string $class The classes. * @param string $icon The icon used. * * @return mixed|string */ public function add_menu_icon_classes( $class = '', $icon = '' ) { if ( empty( $icon ) ) { $icon = Mods::get( $this->get_id() . '_' . self::MENU_ICON, 'default' ); } if ( $icon !== 'default' ) { $add_class = 'hamburger--' . $icon; if ( strpos( $class, $add_class ) === false ) { $class .= $add_class; } } return $class; } /** * Load Component Scripts * * @return void */ public function load_scripts() { if ( $this->is_component_active() || is_customize_preview() ) { wp_add_inline_style( 'neve-style', $this->toggle_style() ); wp_add_inline_script( 'neve-script', $this->toggle_script() ); } } /** * Get JS to use as inline script * * @return string */ public function toggle_script() { // Add aria-expanded to toggle function to be used when the menu icon is used, $script = <<<JS function toggleAriaClick() { function toggleAriaExpanded(toggle = 'true') { document.querySelectorAll('button.navbar-toggle').forEach(function(el) { if ( el.classList.contains('caret-wrap') ) { return; } el.setAttribute('aria-expanded', 'true' === el.getAttribute('aria-expanded') ? 'false' : toggle); }); } toggleAriaExpanded(); if ( document.body.hasAttribute('data-ftrap-listener') ) { return; } document.body.setAttribute('data-ftrap-listener', 'true'); document.addEventListener('ftrap-end', function() { toggleAriaExpanded('false'); }); } JS; // use Dynamic_Css class to minify the script, by removing the whitespace. return Dynamic_Css::minify_css( $script ); } /** * Get CSS to use as inline script * * @return string */ public function toggle_style() { $menu_icon = Mods::get( $this->get_id() . '_' . self::MENU_ICON, 'default' ); $css = $this->get_menu_style( $menu_icon ); return Dynamic_Css::minify_css( $css ); } /** * Generate style specific to menu icon selected. * * @param string $menu_icon The menu icon option. * * @return string */ private function get_menu_style( $menu_icon ) { // We don't add any css for the default option. if ( ! in_array( $menu_icon, [ 'arrow', 'donner', 'dots', 'minus', 'vortex', 'squeeze', 'svg' ] ) ) { return ''; } // Basic styles for custom animations $css = <<<CSS .hamburger { transition-property: opacity, filter; transition-duration: 0.5s; transition-timing-function: linear; } .hamburger .hamburger-inner, .hamburger .hamburger-inner::before, .hamburger .hamburger-inner::after { background-color: currentColor; } .hamburger-box { width: 15px; height: 12px; display: inline-block; position: relative; } .hamburger-inner { display: block; top: 50%; margin-top: -1px; } .hamburger-inner, .hamburger-inner:before, .hamburger-inner:after { width: 15px; height: 2px; background-color: currentColor; border-radius: 2px; position: absolute; transition-property: transform; transition-duration: 0.5s; transition-timing-function: ease; } .hamburger-inner:before, .hamburger-inner:after { content: ""; display: block; } .hamburger-inner:before { top: -5px; } .hamburger-inner:after { bottom: -5px; } CSS; // Arrow style if ( $menu_icon === 'arrow' ) { $css .= <<<CSS .is-active .hamburger--arrow .hamburger-inner:before, .hamburger--arrow.is-active .hamburger-inner:before { transform: translate3d(-3px, 1px, 0) rotate(-45deg) scale(0.7, 1); } .is-active .hamburger--arrow .hamburger-inner:after, .hamburger--arrow.is-active .hamburger-inner:after { transform: translate3d(-3px, -1px, 0) rotate(45deg) scale(0.7, 1); } CSS; } // Donner style if ( $menu_icon === 'donner' ) { $css .= <<<CSS .hamburger--donner .hamburger-inner { transition-duration: 0.075s; transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } .hamburger--donner .hamburger-inner:before { width: 16px; left: -3px; } .hamburger--donner .hamburger-inner { width: 10px; left: 3px; } .hamburger--donner .hamburger-inner:after { width: 4px; left: 3px; } .hamburger--donner .hamburger-inner::before { transition: top 0.075s 0.12s ease, opacity 0.075s ease; } .hamburger--donner .hamburger-inner::after { transition: bottom 0.075s 0.12s ease, transform 0.075s cubic-bezier(0.55, 0.055, 0.675, 0.19); } .is-active .hamburger--donner .hamburger-inner, .hamburger--donner.is-active .hamburger-inner { transform: rotate(45deg); transition-delay: 0.12s; transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } .is-active .hamburger--donner .hamburger-inner, .hamburger--donner.is-active .hamburger-inner, .is-active .hamburger--donner .hamburger-inner:before, .hamburger--donner.is-active .hamburger-inner:before, .is-active .hamburger--donner .hamburger-inner:after, .hamburger--donner.is-active .hamburger-inner:after { width: 15px; left: auto; } .is-active .hamburger--donner .hamburger-inner::before, .hamburger--donner.is-active .hamburger-inner::before { top: 0; opacity: 0; transition: top 0.075s ease, opacity 0.075s 0.12s ease; } .is-active .hamburger--donner .hamburger-inner::after, .hamburger--donner.is-active .hamburger-inner::after { bottom: 0; transform: rotate(-90deg); transition: bottom 0.075s ease, transform 0.075s 0.12s cubic-bezier(0.215, 0.61, 0.355, 1); } CSS; } // Dots style if ( $menu_icon === 'dots' ) { $css .= <<<CSS .hamburger--dots .hamburger-inner { left: 5px; } .hamburger--dots .hamburger-inner, .hamburger--dots .hamburger-inner:before, .hamburger--dots .hamburger-inner:after { width: 4px; border-radius: 2px; } .hamburger--dots .hamburger-inner:before, .hamburger--dots .hamburger-inner:after { transition: bottom 0.08s 0s ease-out, top 0.08s 0s ease-out, opacity 0s linear; } .is-active .hamburger--dots .hamburger-inner, .hamburger--dots.is-active .hamburger-inner { left: inherit; } .is-active .hamburger--dots .hamburger-inner, .hamburger--dots.is-active .hamburger-inner, .is-active .hamburger--dots .hamburger-inner:before, .hamburger--dots.is-active .hamburger-inner:before, .is-active .hamburger--dots .hamburger-inner:after, .hamburger--dots.is-active .hamburger-inner:after { width: 15px; } .is-active .hamburger--dots .hamburger-inner:before, .hamburger--dots.is-active .hamburger-inner:before, .is-active .hamburger--dots .hamburger-inner:after, .hamburger--dots.is-active .hamburger-inner:after { opacity: 0; transition: bottom 0.08s ease-out, top 0.08s ease-out, opacity 0s 0.08s linear; } .is-active .hamburger--dots .hamburger-inner:before, .hamburger--dots.is-active .hamburger-inner:before { top: 0; } .is-active .hamburger--dots .hamburger-inner:after, .hamburger--dots.is-active .hamburger-inner:after { bottom: 0; } CSS; } // Minus style if ( $menu_icon === 'minus' ) { $css .= <<<CSS .hamburger--minus .hamburger-inner:before, .hamburger--minus .hamburger-inner:after { transition: bottom 0.08s 0s ease-out, top 0.08s 0s ease-out, opacity 0s linear; } .is-active .hamburger--minus .hamburger-inner:before, .hamburger--minus.is-active .hamburger-inner:before, .is-active .hamburger--minus .hamburger-inner:after, .hamburger--minus.is-active .hamburger-inner:after { opacity: 0; transition: bottom 0.08s ease-out, top 0.08s ease-out, opacity 0s 0.08s linear; } .is-active .hamburger--minus .hamburger-inner:before, .hamburger--minus.is-active .hamburger-inner:before { top: 0; } .is-active .hamburger--minus .hamburger-inner:after, .hamburger--minus.is-active .hamburger-inner:after { bottom: 0; } CSS; } // Vortex style if ( $menu_icon === 'vortex' ) { $css .= <<<CSS .hamburger--vortex .hamburger-inner { transition-duration: 0.2s; transition-timing-function: cubic-bezier(0.19, 1, 0.22, 1); } .hamburger--vortex .hamburger-inner:before, .hamburger--vortex .hamburger-inner:after { transition-duration: 0s; transition-delay: 0.1s; transition-timing-function: linear; } .hamburger--vortex .hamburger-inner:before { transition-property: top, opacity; } .hamburger--vortex .hamburger-inner::after { transition-property: bottom, transform; } .is-active .hamburger--vortex .hamburger-inner, .hamburger--vortex.is-active .hamburger-inner { transform: rotate(765deg); transition-timing-function: cubic-bezier(0.19, 1, 0.22, 1); } .is-active .hamburger--vortex .hamburger-inner:before, .hamburger--vortex.is-active .hamburger-inner:before, .is-active .hamburger--vortex .hamburger-inner:after, .hamburger--vortex.is-active .hamburger-inner:after { transition-delay: 0s; } .is-active .hamburger--vortex .hamburger-inner:before, .hamburger--vortex.is-active .hamburger-inner:before { top: 0; opacity: 0; } .is-active .hamburger--vortex .hamburger-inner:after, .hamburger--vortex.is-active .hamburger-inner:after { bottom: 0; transform: rotate(90deg); } CSS; } // SVG style if ( $menu_icon === 'svg' ) { $menu_icon_size = Mods::get( $this->get_id() . '_' . self::SIZE_ID, '15' ) . 'px'; $css .= <<<CSS .hamburger-box.icon-svg { width: 100%; height: var(--menuiconsize, $menu_icon_size); display:flex; justify-content:center; } CSS; } // Squeeze style if ( $menu_icon === 'squeeze' ) { $css .= <<<CSS .hamburger--squeeze .hamburger-inner { transition-duration: 0.075s; transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); } .hamburger--squeeze .hamburger-inner::before { transition: top 0.075s 0.12s ease, opacity 0.075s ease; } .hamburger--squeeze .hamburger-inner::after { transition: bottom 0.075s 0.12s ease, transform 0.075s cubic-bezier(0.55, 0.055, 0.675, 0.19); } .is-active .hamburger--squeeze .hamburger-inner, .hamburger--squeeze.is-active .hamburger-inner { transform: rotate(45deg); transition-delay: 0.12s; transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); } .is-active .hamburger--squeeze .hamburger-inner::before, .hamburger--squeeze.is-active .hamburger-inner::before { top: 0; opacity: 0; transition: top 0.075s ease, opacity 0.075s 0.12s ease; } .is-active .hamburger--squeeze .hamburger-inner::after, .hamburger--squeeze.is-active .hamburger-inner::after { bottom: 0; transform: rotate(-90deg); transition: bottom 0.075s ease, transform 0.075s 0.12s cubic-bezier(0.215, 0.61, 0.355, 1); } CSS; } return $css; } /** * Called to register component controls. * * @since 1.0.0 * @access public */ public function add_settings() { SettingsManager::get_instance()->add( [ 'id' => self::TEXT_ID, 'group' => $this->get_id(), 'transport' => 'postMessage', 'tab' => SettingsManager::TAB_GENERAL, 'sanitize_callback' => 'wp_filter_nohtml_kses', 'default' => '', 'label' => __( 'Menu label', 'neve' ), 'type' => 'text', 'section' => $this->section, 'live_refresh_selector' => $this->default_selector . ' .nav-toggle-label', 'live_refresh_css_prop' => array( 'parent' => $this->default_selector, 'wrap_class' => 'nav-toggle-label', 'html_tag' => 'span', ), 'conditional_header' => true, ] ); SettingsManager::get_instance()->add( [ 'id' => self::QUICK_LINKS_ID, 'group' => $this->get_id(), 'transport' => 'postMessage', 'tab' => SettingsManager::TAB_GENERAL, 'sanitize_callback' => 'wp_filter_nohtml_kses', 'type' => '\Neve\Customizer\Controls\React\Instructions_Control', 'section' => $this->section, 'options' => [ 'options' => array( 'quickLinks' => array( 'toggle_sidebar' => array( 'label' => esc_html__( 'Show Mobile Menu', 'neve' ), 'icon' => 'dashicons-nametag', ), 'hfg_header_layout_sidebar_layout' => array( 'label' => esc_html__( 'Mobile Menu Options', 'neve' ), 'icon' => 'dashicons-admin-appearance', ), ), ), ], ] ); SettingsManager::get_instance()->add( [ 'id' => self::LABEL_MARGIN_ID, 'group' => $this->get_id(), 'transport' => 'postMessage', 'tab' => SettingsManager::TAB_LAYOUT, 'sanitize_callback' => array( $this, 'sanitize_spacing_array' ), 'label' => __( 'Margin', 'neve' ) . ' (' . __( 'Label', 'neve' ) . ')', 'type' => '\Neve\Customizer\Controls\React\Spacing', 'default' => $this->default_label_margin_value, 'live_refresh_selector' => '.builder-item--' . $this->get_id(), 'live_refresh_css_prop' => array( 'cssVar' => [ 'vars' => '--label-margin', 'responsive' => true, 'selector' => '.builder-item--' . $this->get_id(), ], 'prop' => 'margin', ), 'section' => $this->section, 'conditional_header' => $this->get_builder_id() === 'header', ] ); SettingsManager::get_instance()->add( [ 'id' => self::MENU_ICON, 'group' => $this->get_id(), 'tab' => SettingsManager::TAB_STYLE, 'transport' => 'refresh', 'sanitize_callback' => 'wp_filter_nohtml_kses', 'label' => __( 'Icon', 'neve' ), 'description' => __( 'Icon', 'neve' ), 'type' => 'Neve\Customizer\Controls\React\Inline_Select', 'default' => 'default', 'options' => [ 'options' => [ 'default' => 'Default', 'arrow' => 'Arrow', 'donner' => 'Donner', 'dots' => 'Dots', 'minus' => 'Minus', 'vortex' => 'Vortex', 'squeeze' => 'Squeeze', ], 'default' => 'default', ], 'section' => $this->section, 'live_refresh_selector' => $this->default_selector, 'conditional_header' => true, ] ); SettingsManager::get_instance()->add( [ 'id' => self::SIZE_ID, 'group' => $this->get_id(), 'tab' => SettingsManager::TAB_STYLE, 'transport' => 'postMessage', 'sanitize_callback' => 'absint', 'default' => 15, 'label' => __( 'Icon Size', 'neve' ), 'type' => 'Neve\Customizer\Controls\React\Range', 'options' => [ 'active_callback' => function () { return Mods::get( $this->get_id() . '_' . self::MENU_ICON, 'default' ) === 'svg'; }, 'priority' => 11, 'input_attrs' => [ 'min' => 10, 'max' => 100, 'defaultVal' => 15, ], ], 'live_refresh_selector' => $this->default_selector . ' span.icon-svg', 'live_refresh_css_prop' => [ 'cssVar' => [ 'vars' => '--menuiconsize', 'selector' => '.builder-item--' . $this->get_id(), 'suffix' => 'px', ], 'type' => 'svg-icon-size', 'default' => 15, ], 'section' => $this->section, 'conditional_header' => true, ] ); $mod_key = self::BUTTON_APPEARANCE; $default = [ 'type' => 'outline', 'borderRadius' => [ 'top' => 0, 'left' => 0, 'bottom' => 0, 'right' => 0, ], ]; SettingsManager::get_instance()->add( [ 'id' => $mod_key, 'group' => $this->get_id(), 'transport' => 'postMessage', 'tab' => SettingsManager::TAB_STYLE, 'sanitize_callback' => 'neve_sanitize_button_appearance', 'default' => $default, 'label' => __( 'Appearance', 'neve' ), 'type' => '\Neve\Customizer\Controls\React\Button_Appearance', 'section' => $this->section, 'options' => [ 'no_hover' => true, 'no_shadow' => true, 'default_vals' => $default, ], 'live_refresh_selector' => $this->default_selector, 'live_refresh_css_prop' => [ 'cssVar' => [ 'vars' => [ '--bgcolor' => 'background', '--color' => 'text', '--borderradius' => [ 'key' => 'borderRadius', 'suffix' => 'px', ], '--borderwidth' => [ 'key' => 'borderWidth', 'suffix' => 'px', ], ], 'selector' => '.builder-item--' . $this->get_id(), ], 'additional_buttons' => $this->get_class_const( 'COMPONENT_ID' ) !== 'header_menu_icon' ? [] : [ [ 'button' => $this->close_button, 'text' => '.icon-bar', ], ], ], 'conditional_header' => true, ] ); } /** * Add CSS style for the component. * * @param array $css_array the css style array. * * @return array */ public function add_style( array $css_array = array() ) { $id = $this->get_id() . '_' . self::BUTTON_APPEARANCE; $rules = [ '--bgcolor' => $id . '.background', '--color' => $id . '.text', '--borderradius' => [ Dynamic_Selector::META_KEY => $id . '.borderRadius', 'directional-prop' => Config::CSS_PROP_BORDER_RADIUS, ], '--borderwidth' => [ Dynamic_Selector::META_KEY => $id . '.borderWidth', 'directional-prop' => Config::CSS_PROP_BORDER_WIDTH, ], ]; $value = SettingsManager::get_instance()->get( $id ); if ( isset( $value['type'] ) && $value['type'] !== 'outline' ) { $rules ['--borderwidth']['override'] = 0; } $css_array[] = [ Dynamic_Selector::KEY_SELECTOR => '.builder-item--' . $this->get_id() . ',' . $this->close_button, Dynamic_Selector::KEY_RULES => $rules, ]; $css_array[] = [ Dynamic_Selector::KEY_SELECTOR => '.builder-item--' . $this->get_id(), Dynamic_Selector::KEY_RULES => [ '--label-margin' => [ Dynamic_Selector::META_KEY => $this->get_id() . '_' . self::LABEL_MARGIN_ID, Dynamic_Selector::META_IS_RESPONSIVE => true, Dynamic_Selector::META_DEFAULT => $this->default_label_margin_value, Dynamic_Selector::META_SUFFIX => 'responsive_unit', 'directional-prop' => Config::CSS_PROP_MARGIN, ], ], ]; return parent::add_style( $css_array ); } /** * Render method. * * @since 1.0.0 * @access public */ public function render_component() { Main::get_instance()->load( 'components/component-menu-icon' ); } /** * Static method to return the aria expanded behaviour for the menu buttons. * This will add the necessary attributes to the button to toggle the aria-expanded attribute and the onclick event. * It is added inline to not increase the JS file size. Will only be added when the menu item is rendered. * * @param boolean $only_click_attribute Flag to return only the click attribute. * * @return string */ public static function aria_expanded_behaviour( $only_click_attribute = false ) { if ( neve_is_amp() ) { return $only_click_attribute ? '' : 'aria-expanded="false"'; } return ( $only_click_attribute ? '' : 'aria-expanded="false" ' ) . 'onclick="if(\'undefined\' !== typeof toggleAriaClick ) { toggleAriaClick() }"'; } }