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ая строка это т, что в окне редактора будет вставлено вместо редактируемой строки.
Заключение
Было разработанное расширение для Элементор. Код сможешь глянуть здесь, конечно он не полный(я удалил из него интеграции с другими плагинами, кастомными плагинами и сократил дерево настроек). Больше примеров ты увидишь в самом Элементор, ну а я попытался немного объяснить – что и как там работает.