Перейти к содержанию

Системное меню

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

Компонент используют для контекстных действий над объектом или выбора одного варианта из короткого списка. Меню закрывается по клику вне области, по Esc и после клика по пункту, если это не переопределено в параметрах.

В Bitrix Framework за системное меню отвечает расширение ui.system.menu. В нем доступны класс Menu и константы для оформления пунктов, секций и заголовка. Для Vue-компонента используется отдельное расширение ui.system.menu.vue.

Подключить расширение

Если вы подключаете класс Menu из PHP, загрузите расширение ui.system.menu.

\Bitrix\Main\UI\Extension::load('ui.system.menu');

Если вы работаете в модульном JavaScript, импортируйте класс и константы из ui.system.menu.

import { Menu, MenuItemDesign } from 'ui.system.menu';

Для иконок в пунктах меню используйте значения из ui.icon-set.api.core.

import { Outline } from 'ui.icon-set.api.core';

Создать меню

Чтобы создать меню, выполните основные действия:

  1. Создайте экземпляр Menu.

  2. Передайте массив items с пунктами меню.

  3. Вызовите show(bindElement), чтобы открыть меню рядом с элементом страницы.

import { Menu, MenuItemDesign } from 'ui.system.menu';

const button = document.getElementById('actions-button');

const menu = new Menu({
    items: [
        {
            id: 'edit',
            title: 'Редактировать',
            onClick: () => {
                console.log('Редактировать объект');
            },
        },
        {
            id: 'delete',
            title: 'Удалить',
            design: MenuItemDesign.Alert,
            onClick: () => {
                console.log('Удалить объект');
            },
        },
    ],
});

menu.show(button);

Передать параметры меню

Конструктор Menu принимает объект с параметрами меню. Если вызвать show() без аргумента, компонент откроет меню рядом с bindElement.

bindElement — это DOM-элемент, рядом с которым нужно открыть меню. Обычно его получают из страницы через document.getElementById() или из Vue-шаблона через ref.

  • items — массив пунктов меню.

  • sections — массив секций. Секция группирует пункты по значению sectionCode.

  • richHeader — крупный заголовок в верхней части меню. Объект может содержать design, title, subtitle, icon и onClick.

  • closeOnItemClick — закрывать меню после клика по пункту без вложенного меню. По умолчанию true.

Настроить пункт меню

Пункт меню задается объектом внутри массива items.

  • id — идентификатор пункта. Используется, чтобы отличать пункты при обновлении списка.

  • title — основной текст пункта.

  • subtitle — дополнительный текст под названием.

  • onClick — обработчик клика по пункту.

  • design — оформление пункта, значение из MenuItemDesign.

  • sectionCode — код секции, в которой нужно вывести пункт.

  • icon — иконка справа, например значение из Outline.

  • svg — SVG-элемент справа. Используется вместо icon, если icon не передан.

  • badgeText — короткий текстовый бейдж рядом с названием. Объект содержит title и необязательный color.

  • isSelected — состояние выбора. Значение true показывает галочку, false оставляет место под галочку пустым.

  • isLocked — значок замка перед названием.

  • counter — параметры счетчика из ui.cnt. Объект передается в компонент Counter. Для простого счетчика передайте value.

  • extraIcon — дополнительная иконка справа. Объект содержит icon, onClick и isSelected.

  • subMenu — параметры вложенного меню. Объект принимает те же параметры, что и Menu.

  • closeOnSubItemClick — закрывать родительское меню после клика по пункту вложенного меню. По умолчанию true.

  • uiButtonOptions — параметры кнопки из ui.buttons. Объект передается в компонент Button. Для простого пункта-кнопки передайте text и onclick.

import { Menu, MenuItemDesign, MenuSectionDesign } from 'ui.system.menu';
import { Outline } from 'ui.icon-set.api.core';

const targetElement = document.getElementById('menu-button');

const createItem = ({ id, subtitle, design, extraIconSelected = true }) => ({
    id,
    title: 'Название',
    subtitle,
    design,
    badgeText: {
        title: 'новое',
    },
    isLocked: true,
    isSelected: true,
    extraIcon: {
        icon: Outline.CHEVRON_RIGHT_L,
        isSelected: extraIconSelected,
    },
    counter: {
        value: 1,
    },
    icon: Outline.PERSON,
});

const menu = new Menu({
    sections: [
        {
            code: 'accent',
            title: 'Секция с акцентом',
            design: MenuSectionDesign.Accent,
        },
        {
            code: 'default',
            title: 'Обычная секция',
        },
    ],
    items: [
        createItem({
            id: 'default',
            subtitle: 'Подназвание (Default)',
            extraIconSelected: false,
        }),
        createItem({
            id: 'accent-1',
            subtitle: 'Подназвание (Accent1)',
            design: MenuItemDesign.Accent1,
        }),
        createItem({
            id: 'accent-2',
            subtitle: 'Подназвание (Accent2)',
            design: MenuItemDesign.Accent2,
        }),
        createItem({
            id: 'alert',
            subtitle: 'Подназвание (Alert)',
            design: MenuItemDesign.Alert,
        }),
        createItem({
            id: 'bitrix-gpt',
            subtitle: 'Подназвание (BitrixGPT)',
            design: MenuItemDesign.Copilot,
        }),
        createItem({
            id: 'disabled',
            subtitle: 'Подназвание (Disabled)',
            design: MenuItemDesign.Disabled,
            extraIconSelected: false,
        }),
        {
            id: 'recommended',
            title: 'Название',
            subtitle: 'Подназвание',
            badgeText: {
                title: 'рекомендуется',
                color: '#0B66FF',
            },
        },
        {
            id: 'delete',
            title: 'Удалить',
            icon: Outline.TRASHCAN,
            design: MenuItemDesign.Alert,
        },
    ],
});

menu.show(targetElement);

Выбрать оформление пункта

Оформление меняет цвет текста и служебных иконок пункта. Используйте константы MenuItemDesign, а не строковые значения.

  • MenuItemDesign.Default — обычный пункт без цветового акцента: темный текст и нейтральные служебные иконки. Используется для нейтральных действий.

  • MenuItemDesign.Accent1 — окрашивает служебные иконки в основной акцентный цвет, но оставляет текст нейтральным. Подходит для действия, которое нужно выделить без усиления текста.

  • MenuItemDesign.Accent2 — окрашивает текст и служебные иконки в основной акцентный цвет. Подходит для основного или рекомендуемого действия в меню.

  • MenuItemDesign.Alert — окрашивает текст и служебные иконки в красный цвет. Используется для опасного или критичного действия, например удаления.

  • MenuItemDesign.Copilot — окрашивает текст и служебные иконки в цвета BitrixGPT. Используйте для действий, связанных с BitrixGPT.

  • MenuItemDesign.Disabled — окрашивает текст и служебные иконки в приглушенный серый цвет, чтобы показать недоступный пункт.

MenuItemDesign.Disabled меняет оформление пункта и не открывает вложенное меню при наведении. Этот режим не отменяет обработчик onClick. Если пункт не должен выполнять действие, не передавайте ему обработчик или обработайте блокировку в своем коде.

Добавить секции

Секции помогают разделить пункты по группам. У секции должен быть code, а у пункта — такой же sectionCode.

import { Menu, MenuSectionDesign } from 'ui.system.menu';

const menu = new Menu({
    sections: [
        {
            code: 'main',
            title: 'Основные действия',
            design: MenuSectionDesign.Accent,
        },
        {
            code: 'service',
            title: 'Служебные действия',
        },
    ],
    items: [
        {
            id: 'open',
            sectionCode: 'main',
            title: 'Открыть',
            onClick: () => {},
        },
        {
            id: 'copy-link',
            sectionCode: 'service',
            title: 'Скопировать ссылку',
            onClick: () => {},
        },
    ],
});

Пункты без sectionCode выводятся до секций. Если секция указана, но в ней нет пунктов, компонент ее не выводит.

Доступные варианты оформления секции:

  • MenuSectionDesign.Default — обычный заголовок секции: небольшой серый текст и разделитель.

  • MenuSectionDesign.Accent — акцентный заголовок секции: текст крупнее и плотнее, разделитель остается нейтральным.

Добавить заголовок меню

Параметр richHeader добавляет верхний блок с крупной иконкой, заголовком, подзаголовком и необязательной иконкой справа.

import { Menu, MenuRichHeaderDesign } from 'ui.system.menu';
import { Outline } from 'ui.icon-set.api.core';

const menu = new Menu({
    richHeader: {
        design: MenuRichHeaderDesign.Copilot,
        title: 'BitrixGPT',
        subtitle: 'Помощник',
        icon: Outline.CHEVRON_RIGHT_L,
        onClick: () => {
            console.log('Открыть раздел BitrixGPT');
        },
    },
    items: [
        {
            id: 'create-text',
            title: 'Создать текст',
            onClick: () => {},
        },
    ],
});

Доступные варианты оформления:

  • MenuRichHeaderDesign.Default — обычный заголовок с мягким синим фоном, синей иконкой и акцентным цветом текста.

  • MenuRichHeaderDesign.Copilot — заголовок в цветах BitrixGPT: фиолетовый фон, фиолетовая иконка и акцентный цвет текста. Используйте для сценариев BitrixGPT.

Если subtitle, icon или onClick не переданы, соответствующая часть заголовка не используется.

Добавить вложенное меню

Чтобы открыть вложенное меню при наведении, передайте в пункт параметр subMenu.

import { Menu } from 'ui.system.menu';

const menu = new Menu({
    items: [
        {
            id: 'move',
            title: 'Переместить',
            subMenu: {
                items: [
                    {
                        id: 'move-to-active',
                        title: 'В активные',
                        onClick: () => {},
                    },
                    {
                        id: 'move-to-archive',
                        title: 'В архив',
                        onClick: () => {},
                    },
                ],
            },
        },
    ],
});

Если у пункта есть subMenu, клик по нему не закрывает родительское меню — параметр closeOnItemClick его не затрагивает. Закрытие при клике по пункту вложенного меню управляется отдельным параметром closeOnSubItemClick у родительского пункта.

Добавить дополнительную иконку

Клик по extraIcon вызывает только обработчик этой иконки и не вызывает onClick основного пункта.

import { Menu } from 'ui.system.menu';
import { Outline } from 'ui.icon-set.api.core';

const menu = new Menu({
    items: [
        {
            id: 'filter',
            title: 'Фильтр',
            extraIcon: {
                icon: Outline.CHEVRON_RIGHT_L,
                isSelected: true,
                onClick: () => {
                    console.log('Настроить фильтр');
                },
            },
            onClick: () => {
                console.log('Выбрать фильтр');
            },
        },
    ],
});

По умолчанию дополнительная иконка появляется при наведении на пункт. Чтобы показывать ее постоянно, передайте extraIcon.isSelected: true.

Использовать кнопку как пункт меню

Если нужно встроить кнопку из ui.buttons, передайте параметры кнопки в uiButtonOptions.

В этом режиме компонент не использует стандартные параметры пункта title, subtitle, icon, counter и onClick.

Параметры кнопки задают текст и обработчик клика.

import { Menu } from 'ui.system.menu';

const menu = new Menu({
    items: [
        {
            id: 'create',
            uiButtonOptions: {
                text: 'Создать',
                onclick: () => {
                    console.log('Создать объект');
                },
            },
        },
    ],
});

Управлять меню

Используйте методы Menu, чтобы обновлять пункты и закрывать уже созданное меню.

  • show(bindElement) — показывает меню рядом с переданным элементом или рядом с bindElement из параметров.

  • close() — закрывает текущее меню.

  • destroy() — удаляет меню и вложенные меню. Чтобы снова показать меню после destroy(), создайте новый экземпляр Menu.

  • updateItems(itemsOptions) — заменяет список пунктов в уже созданном меню.

import { Menu } from 'ui.system.menu';

const menu = new Menu({
    closeOnItemClick: false,
    items: [
        {
            id: 'draft',
            title: 'Черновик',
            isSelected: true,
            onClick: () => {},
        },
    ],
});

menu.show(document.getElementById('status-button'));

menu.updateItems([
    {
        id: 'published',
        title: 'Опубликовано',
        isSelected: true,
        onClick: () => {},
    },
]);

Использовать Vue-компонент

В расширении ui.system.menu.vue доступен Vue-компонент BMenu. Он принимает options с теми же параметрами, что и класс Menu.

Когда меню закрывается, BMenu вызывает событие close.

Если Vue-компонент используется на странице, загрузите расширение ui.system.menu.vue из PHP.

\Bitrix\Main\UI\Extension::load('ui.system.menu.vue');

В модульном JavaScript импортируйте компонент BMenu.

import { BMenu } from 'ui.system.menu.vue';

export const ActionsMenu = {
    components: { BMenu },
    data()
    {
        return {
            isMenuShown: false,
        };
    },
    computed: {
        menuOptions()
        {
            return {
                bindElement: this.$refs.actionsButton,
                items: [
                    {
                        id: 'edit',
                        title: 'Редактировать',
                        onClick: () => {},
                    },
                ],
            };
        },
    },
    template: `
        <button ref="actionsButton" @click="isMenuShown = true">
            Действия
        </button>
        <BMenu
            v-if="isMenuShown"
            :options="menuOptions"
            @close="isMenuShown = false"
        />
    `,
};

Совет

Подробнее о работе с Vue в Bitrix Framework читайте в статье Vue.js.