Elementor Addon. Пишем кастомный виджет к билдеру

Elementor – популярный wp плагин для построения страниц; данный тип плагинов называют pagebuilder. Это плагин(как и все в его категории) оперирует специальными виджетами – именно его я и создам, по ходу статьи…

Содержание

Предисловие

Здесья расскажу о том, что толкнуло меня на создание виджета(помимо получения опыта для написания этой писульки). Дело было так: была страница на которую выводилось видео с YouTube. К нему был список тайм-кодов, клик по которым включал воспроизведение видео с определенного момента. Временная метка(таймкод) состоит из самого времени, картинки и текста-описания. При переезде страницы на Элементор – всё взаимодействие сламалось. Задача: создать аддон к Элементор, добавляющий эти возможности в виджете.

Немного воды…

Всем, кто знаком с веб-программированием, с ходу стало понятно в чем причина – javascript; теперь для тех, кто незнаком: за прослушивание всех событий в браузере отвечает javascript. И правда, билдер изменил структуру DOM-дерева страницы и, за часик(долго разбирался в новой структуре), изменив селекторы в скрипте все заработало…

И что ж?.. на этом все? Было бы всё, если бы владелец не сказал, что неудобно так: строить ряд, в нем колонки с текстовым полем в(которых снова ряды с колонками) – сделай-ка единым блоком, чтоб в элементы управления просто вбить значения.

Приступим…

Начал я, разумеется, с оболочки плагина. На сайте плагина, в доках, есть шаблон кода основного файла для аддонов Элементор. Немного опишу главный метод, который в нем присутствует

add_action('elementor/init', function () {
    add_action('elementor/widgets/widgets_registered', function () {
        require_once(__DIR__ . '/class-youtube-widget.php');
        ElementorPlugin::instance()->widgets_manager->register_widget_type(new Elementor_YouTube_Widget());
    });
});

чтоб написать любое расширение к элементору нужно расширить его класс; чтобы получить доступ к ядру плагина(а именно там лежат они) нужно вызывать аддон после загрузки плагина. В движке есть хук(plugins_loaded), срабатывающий после загрузки всех плагинов – однако, в виду того, как устроен сам Элементор – в этот момент его ядро уже недоступно.

Плагин Элементор имеет свои хуки, позволяющие получить доступ к разным классам его ядра:

  • elementor/init – ко всем;
  • ementor/widgets/widgets_registered – к классу ElementorWidget_Base(и прочим, необходимым для создания нового виджета);
  • elementor/controls/control_registered – к классу ElementorControl_Base(и прочим, необходимым для создания нового типа контролера для плагина);

Это все хуки, которые нужно знать, для того чтоб построить свой виджет. На деле, из-за довольно широкой палитры встроенных контролей, 3ий хук я никогда не использовал – не было нужды создавать свои контроллеры.

Теперь можно переходить к созданию самого виджета… хотя, нет – пару строк о скрипте. Здесь, как и в вордпресс, нет метода, который подключал бы скрипты только когда виджет есть на странице(по крайней мере я таких не увидел – если ты о них знаешь – напиши в комментах, буду благодарен!) и потому я подключаю их как обычные скрипты.

Теперь немного о самом скрипте – в нем я буду использовать YouTube Iframe Player API. Начнем с html-разметки, которую будем строить на странице

        <section>
            <h2>{{ section_title }}</h2>
            <div class="elementor-row {{ video_position }}" data-id="{{ video_id }}">
                <div class="elementor-column frame-container">
                    <img src="https://img.youtube.com/vi/{{ video_id }}/hqdefault.jpg" class="youtube-preview" alt="Video Preview">
                </div>
                <div class="elementor-column">
                    {{ timestamps_loop }}
                        <figure class="stamp">
                            <img src="{{ timestamp_image_url }}" alt="Stamp Icon">
                            <span class="timestamp">{{ timestamp_time }}</span>
                            <span class="description">{{ timestamp_description }}</span>
                        </igure>
                    {{ endloop }}
                </div>
            </div>
        </section>

Думал использовать Iframe API, но глянув, что он делает – послал его лесом. Дело в том, что беглый анализ кода показал, что апи содержит методы постройки тега iframe с его атрибутами; учитывая, что страница индексируемая(должна быть оптимизирована под скорость загрузки) я решил не захламлять ее дополнительной библиотекой(jquery и так хорошо выполняет эту задачу). В общем, вот скрипт для работы с метками с вышеприведенной разметкой

(function($) {
    'use strict';

    $(document).on('click', '.youtube-preview', function() {
        var id = $(this).closest('.elementor-row').data('id');

        YTFrame.build(id, 0, $(this).parent());
    });

    $(document).on('click', 'figure.stamp', function() {
        console.log(this);
        var id = $(this).closest('.elementor-row').data('id'),
            time = $(this).find('.timestamp').text().split(':'),
            seconds = 60 * parseInt(time[0]) + parseInt(time[1]);
        console.log($(this).closest('.elementor-row').find('.frame-container')[0]);

        YTFrame.build(id, seconds, $(this).closest('.elementor-row').find('.frame-container')[0]);

    });

    var YTFrame = {
        build: function(id, seconds, container) {
            var iframe = document.createElement('iframe');

            iframe.src = 'https://www.youtube-nocookie.com/embed/' + id + '?autoplay=1&start=' + seconds;
            $(container).html(iframe);
        }
    }
})(jQuery);

Также, данный скрипт позволил обойти некоторые ограничения.

Доделываем

Имея скрипт и накидав разметку, а также оболочку для аддона – создадим класс нашего виджета. Он является дочерним для базового абстрактного виджета элементор(который доступен только в ядре плагина); также, необходимы классы для контроля виджета в редакторе

<?php

use ElementorControls_Manager;
use ElementorWidget_Base;
use ElementorUtils;
use ElementorRepeater;

Теперь создадмим сам класс со следующими методами

<?php
class Elementor_Test_Widget extends ElementorWidget_Base {

	public function get_name() {}

	public function get_title() {}

	public function get_icon() {}

	public function get_categories() {}

	protected function _register_controls() {}

	protected function render() {}

	protected function _content_template() {}

}

Первые 4 методы просто возвращают по строке. Метод render() строит разметку в редакторе(с приметкой) и на сайте

    protected function render()
    {
        $settings = $this->get_settings_for_display();
        $this->add_inline_editing_attributes('title', 'advanced'); ?>
        <section>
            <h2 <?php echo $this->get_render_attribute_string('text_attr') ?>>
                <?php echo $this->get_settings('title') ?>
            </h2>
            <div class="elementor-row  <?php echo esc_attr($settings['video_position']) ?>" data-id="<?php echo esc_attr($settings['video_id']) ?>">
                <div class="elementor-column frame-container">
                    <img src="<?php printf('https://img.youtube.com/vi/%s/hqdefault.jpg', esc_attr($settings['video_id'])) ?>" class="youtube-preview" alt="Video Preview">
                </div>
                <div class="elementor-column">
                    <?php foreach ($settings['timestams_repeater'] as $key => $value): ?>
                        <figure class="stamp">
                            <img src="<?php echo esc_url($value['stamp_image']['ur']) ?>" alt="Stamp Icon">
                            <span class="timestamp"><?php echo esc_html($value['time_stamp']) ?></span>
                            <span class="description"><?php echo esc_html($value['stamp_description']) ?></span>
                        </igure>
                    <?php endforeach; ?>
                </div>
            </div>
        </section>
        <?php
    }

Здесь Widget_Base->get_settings_for_display() это получение настроек виджета, которые я создам в следующем методе.

Метод _register_controls() отвечает за создание контролей виджета в сайдбаре редактора – здесь немного поподробней. Все настройки растоложены в 3х табах – контент, стиль и дополнительные(их не трожь – они всегда дефолтные!). чтоб выбрать таб нужно в этом методе написать

        $this->start_controls_section(
            'content_section',
            [
                'label' => __('Video'),
                'tab' => Controls_Manager::TAB_CONTENT,
            ]
        );

затем следует открывающий и закрывающий секцию настроек код

        $this->add_control(
            'title',
            [
                'label' => __('Section Title'),
                'type' => Controls_Manager::TEXT,
                'input_type' => 'text'
            ]
        );

        $this->end_controls_section();

и уже между этими тегами можно писать свою настройку.

Вся “слоенка” будет выглядеть так

        // указание на таб
        //  если TAB_CONTENT поменять на TAB_STYLE
        // будет запись в стиль-таб 
        $this->start_controls_section(
            'content_section',
            [
                'label' => __('Video'),
                'tab' => Controls_Manager::TAB_CONTENT,
            ]
        );

        // открытие новой секции
        $this->add_control(
            'title',
            [
                'label' => __('Section Title'),
                'type' => Controls_Manager::TEXT,
                'input_type' => 'text'
            ]
        );


        // настройка в секции, их может быть много
        $this->add_control(
            'video_id',
            [
                'label' => __('Video ID'),
                'type' => Controls_Manager::TEXT,
                'input_type' => 'text'
            ]
        );

        // закрытие текущей секции, но не таба!
        // после этого можно открыть новую секцию
        // она будет в том же табе
        $this->end_controls_section();

Впринципе, этих методов достаточно для того, чтоб сделать рабочий виджет. Далее пойдет вещь, которую я не делал в этой работе, но хочу показать и рассказать.

Наверняка внимательный читатель не только статьи но и кода, заметил в рендеринг-методе эти строки

$this->add_inline_editing_attributes('title', 'advanced');
$this->get_render_attribute_string('title');
$this->get_settings('title');

все они не имеют смысла без метода _content_template(). Поясню: виджет элементор – блок визуально редактируемый; это значит его можно редактировать в поле редактора, а не в сайдбаре. Кароч, 1ая строка это указатель на то, что эта настройка имееит возможность визуального редактирования; 2ой – получает набор классов для того чтоб javascript элементора взял под контроль этот тег; 3я – получает само значение настройки. В случаи с ‘advanced’ можно увидеть расширенную панель редактирования текста

расширенная панель редактирования текста
расширенная панель редактирования текста

Однако, чтоб все это сработало – необходим вышеуказанный метод с следующими строками

<# view.addInlineEditingAttributes( 'title', 'advanced' ); #>
<div {{{ view.getRenderAttributeString( 'title' ) }}}>{{{ settings.title }}}</div>

Заметь(!) эти строки должны выводиться, тк они будут вставлены в backbone-шаблоны(документация по ним). 2ая строка это т, что в окне редактора будет вставлено вместо редактируемой строки.

Заключение

Было разработанное расширение для Элементор. Код сможешь глянуть здесь, конечно он не полный(я удалил из него интеграции с другими плагинами, кастомными плагинами и сократил дерево настроек). Больше примеров ты увидишь в самом Элементор, ну а я попытался немного объяснить – что и как там работает.

Добавить комментарий

Ваш адрес email не будет опубликован.