Форма ввода — это очень значимый элемент пользовательского интерфейса. При создании собственного интерфейса самым «долгим» моментом является кастомизация списка выбора под стандартный дизайн bitrix24. В этой статье я хочу поделиться js расширением, которое позволит превратить обычный html элемент select в красивый и функциональный компонент. Про создание, подключение и использование js расширений тоже расскажу.
В bitrix24 существует прекрасное js расширение «Диалог выбора сущностей», предоставляющий интерфейс для создания интерактивных диалогов выбора элементов. Элементами выбора могут быть как стандартные данные (такие, как пользователи, элементы различных сущностей CRM и пр.) так и свои собственные. С полным функционалом предоставляемым данным расширением вы можете ознакомиться на странице официальной документации.
Взяв за основу стандартное расширение — создадим новое, задачей которого будет превратить обычный html элемент select в нечто похожее по виду и функционалу. По сути — это будет обёртка над существующей библиотекой.
Шаг 1 создание js расширения (extension)
Js расширение в bitrix24 представляет собой набор javascrit и css файлов. Подгружаемые файлы описываются в конфигурационном файле config.php. Сама директория расширения располагается в папке local/js/ Структура нашего расширения будет следующей:
- local/
-- js/
--- aclips/
---- ui-selector/
----- config.php
----- script.js
Подключение расширения происходит через метод load класса \Bitrix\Main\UI\Extension. В качестве параметра метод принимает название подключаемого расширения. Название расширения формируется из имён вложенных директорий, находящихся в папке local/js (Для справки: стандартные расширения расположены в папке bitrix/js и подключаются аналогичным образом).
Так будет выглядеть подключение нашего расширение, например, в шаблоне компонента:
<?php if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) { die(); } use Bitrix\Main\UI\Extension; Extension::load('aclips.ui-selector');
Заполним конфигурационный файл. В нём нужно указать какие js и css файлы будут загружены при подключении расширения, а также какие зависимости в нём присутствуют. У нас есть зависимость от стандартного ui.entity-selector, нам нужно загрузить файл script.js, файл стилей в нашем примере отсутствует.
<?php if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) { die(); } return [ "js" => [ "./script.js", ], "rel" => [ "ui.entity-selector" ] ];
Шаг 2 Определимся как должен работать плагин
Прежде чем приступить к написанию кода, нужно договориться с самим собой, как этот плагин должен работать. В базовом варианте, нужно указать как можно меньше параметров для упрощения работы с нашим расширением.
Предположим, что нужно передать сам элемент или его идентификатор в функцию, которая преобразит наш select и передать какой-то набор опций вторым параметром. В опциях будем указывать вкладки, сортировку и вообще всё, что нам может понадобиться для настройки наших интерактивных списков. А быть может ничего не будет передавать.
Шаг 3 Пишем задуманное
Ознакомившись с API базового диалога выбора интерфейсов и придерживаясь договорённостей из шага 2, файл script.js получился следующего вида:
BX.namespace('Plugin.UiSelector') /** * Плагин для создания объекта TagSelector на базе статического списка * @type {{createTagSelector: BX.Plugin.UiSelector.createTagSelector, renderTagSelector: BX.Plugin.UiSelector.renderTagSelector}} */ BX.Plugin.UiSelector = { /** * {HTMLElement|String} container HTML element or its ID ("select" tag only) select * {{}} params */ createTagSelector: function (select, params) { let target let tabs = [{id: 'base', title: 'Элементы', itemOrder: {title: 'asc'}}] if (typeof params == 'object') { if (params.tabs) { tabs = params.tabs } } if (typeof select === 'string') { target = document.getElementById(select) if (!target) { throw new Error('Container parameter is not valid ID.') } } else { target = select; } if (target.type != 'select-one' && target.type != 'select-multiple') { throw new Error('Container must be Select') } let options = target.options let items = [] for (let option of options) { if (!option.value) { continue } let tab = 'base' let datatTab = option.getAttribute('data-tab') if (datatTab) { tab = datatTab } let label = option.text if (tabs.length > 1) { let optionTab = tabs.find(e => e.id == tab) if (optionTab) { label = optionTab.title + ': ' + label } } items.push({ id: option.value, title: label, entityId: 'main', tabs: tab, selected: option.selected }) } let config = { node: target, multiple: target.type == 'select-multiple', items: items, tabs: tabs } this.renderTagSelector(config) }, /** * @param {HTMLElement} config.node * @param {boolean} config.multiple * @param {[]} config.items */ renderTagSelector: function (config) { config.node.style.display = 'none' const tagSelector = new BX.UI.EntitySelector.TagSelector({ multiple: config.multiple, dialogOptions: { multiple: config.multiple, items: config.items, selectedItems: config.items.filter(e => e.selected), dropdownMode: true, enableSearch: false, compactView: false, tabs: config.tabs, }, events: { onBeforeTagAdd: function (event) { const {tag} = event.getData(); if(config.multiple){ let options = config.node.querySelectorAll('option[value="'+tag.getId()+'"]') if(options.length > 0) { options[0].selected = true } } else { config.node.value = tag.getId() } config.node.dispatchEvent(new Event('change')) }, onBeforeTagRemove: function (event) { if(config.multiple){ const {tag} = event.getData(); let options = config.node.querySelectorAll('option[value="'+tag.getId()+'"]') if(options.length > 0) { options[0].selected = false } } else { config.node.value = null } config.node.dispatchEvent(new Event('change')) }, } }); tagSelector.renderTo(config.node.parentNode); } }
Шаг 4 Проверка работы
Использование плагина без опций
<?php \Bitrix\Main\UI\Extension::load('aclips.ui-selector') ?> <select id='my_select'> <option value='1'>Option 1</option> <option value='2'>Option 2</option> <option value='3'>Option 3</option> <option selected value='4'>Option 4</option> <option value='5'>Option 5</option> </select> <script type="text/javascript"> BX.ready(function () { BX.Plugin.UiSelector.createTagSelector('my_select') // or BX.Plugin.UiSelector.createTagSelector(document.getElementById('my_select')) }) </script>
Использование плагина с опциями
<?php \Bitrix\Main\UI\Extension::load('aclips.ui-selector') ?> <select id='my_select'> <option data-tab='tab_1' value='1'>Option 1</option> <option data-tab='tab_2' value='2'>Option 2</option> <option data-tab='tab_3' value='3'>Option 3</option> <option data-tab='tab_4' selected value='4'>Option 4</option> <option data-tab='tab_4' value='5'>Option 5</option> </select> <script type="text/javascript"> BX.ready(function () { BX.Plugin.UiSelector.createTagSelector('my_select', { tabs: [ {id: 'tab_1', title: 'Tab 1', itemOrder: {title: 'asc'}}, {id: 'tab_2', title: 'Tab 2', itemOrder: {title: 'asc'}}, {id: 'tab_3', title: 'Tab 3', itemOrder: {title: 'asc'}}, {id: 'tab_4', title: 'Tab 4', itemOrder: {title: 'asc'}} ] }) }) </script>
Итог
Разработав расширение можно надолго забыть о проблеме кастомизации (в нашем случае) элементов select, достаточно подключить плагин в нужном, указать элементы, которые требуется привести к должному виду и наслаждаться результатами своей работы, или потратить время на что-то другое.