Interface: ButtonWithDropdown
Интерфейс для кнопки с ассоциированным выпадающим меню.
Это гибридный элемент управления, сочетающий функциональность обычной кнопки и выпадающего меню. Часто используется в панели инструментов и боковых панелях для группировки связанных действий.
Наследует функциональность от BaseControl для единообразного управления и добавляет поддержку выпадающего списка с вложенными кнопками.
Remarks
Иерархия интерфейсов:
ButtonWithDropdown (текущий интерфейс)
├── extends BaseControl
│ ├── id: string (обязательно)
│ ├── title: string (обязательно)
│ ├── order?: number (опционально)
│ └── groupingId?: string (опционально)
├── extends MaiHaveIcon
│ └── icon?: string (опционально)
├── extends MayBeDisabled
│ └── disabled?: boolean (опционально)
├── extends MayBeChecked
│ └── checked?: boolean (опционально)
├── onClick?: Callback (опционально, для основной кнопки)
└── items: Button[] (обязательно, для выпадающего меню)
Структура и поведение:
┌─────────────────────┬─────┐
│ Название кнопки │ ▼ │ ← ButtonWithDropdown
└─────────────────────┴─────┘
│ клик на кнопку │ клик на стрелку
│ (выполнить onClick) │ (открыть меню)
▼ ▼
Действие ┌──────────────┐
│ Действие 1 │
│ Действие 2 │
│ Действие 3 │
└──────────────┘
Режимы работы:
-
Только выпадающее меню (без onClick)
- Клик на кнопку открывает меню
- Нет самостоятельного действия
- Нет состояния
checked - Стрелка и основная область работают одинаково
-
Двухфункциональная кнопка (с onClick)
- Клик на основную часть выполняет onClick
- Поддержка отдельного состояния
checked - Клик на стрелку открывает меню
- Две разные функции в одном элементе
Когда использовать ButtonWithDropdown:
- Для группировки связанных действий (экспорт в разные форматы)
- Для выбора из вариантов одного действия
- Для экономии места в панели инструментов
- Когда есть основное действие + варианты этого действия
Отличие от других элементов:
- Button — простая кнопка без меню
- ButtonWithDropdown — кнопка с выпадающим меню (текущий)
- Контекстное меню (ContextMenuItem) — появляется при правом клике
Example
Только выпадающее меню (без основного действия)
const exportDropdown: ButtonWithDropdown = {
id: 'plugin:export',
title: 'Экспортировать',
icon: 'Download',
type: 'button-with-dropdown',
items: [
{
id: 'plugin:export-pdf',
title: 'Экспортировать в PDF',
type: 'button',
onClick: editorApi.createCallback(async () => {
// экспорт в PDF
editorApi.ui.toasts.showToast({ id, content: 'Экспорт в PDF...' });
})
},
{
id: 'plugin:export-html',
title: 'Экспортировать в HTML',
type: 'button',
onClick: editorApi.createCallback(async () => {
// экспорт в HTML
editorApi.ui.toasts.showToast({ id, content: 'Экспорт в HTML...' });
})
},
{
id: 'plugin:export-docx',
title: 'Экспортировать в Word',
type: 'button',
onClick: editorApi.createCallback(async () => {
// экспорт в Word
editorApi.ui.toasts.showToast({ id, content: 'Экспорт в Word...' });
})
}
]
};
Двухфункциональная кнопка (с основным действием)
const pasteDropdown: ButtonWithDropdown = {
id: 'plugin:paste',
title: 'Вставить',
icon: 'Paste',
type: 'button-with-dropdown',
// Клик на основную часть выполняет это действие
onClick: editorApi.createCallback(async () => {
await editorApi.document.clipboard.paste();
editorApi.ui.toasts.showToast({ id, content: 'Вставлено' });
}),
// Клик на стрелку открывает варианты вставки
items: [
{
id: 'plugin:paste-normal',
title: 'Обычная вставка',
type: 'button',
onClick: editorApi.createCallback(async () => {
await editorApi.document.clipboard.paste();
})
},
{
id: 'plugin:paste-special',
title: 'Специальная вставка',
type: 'button',
onClick: editorApi.createCallback(async () => {
// специальная вставка
})
},
{
id: 'plugin:paste-unformatted',
title: 'Вставить без форматирования',
type: 'button',
onClick: editorApi.createCallback(async () => {
const text = await editorApi.document.clipboard.getPlainText?.();
if (text) {
await editorApi.document.insertContent(text);
}
})
}
]
};
Получение текста в разных форматах
const getTextDropdown: ButtonWithDropdown = {
id: 'plugin:get-text',
title: 'Get text',
icon: 'GetText',
type: 'button-with-dropdown',
groupingId: 'get-selection',
items: [
{
id: 'plugin:get-text-plain',
title: 'Get as text',
type: 'button',
onClick: editorApi.createCallback(async () => {
const text = await editorApi.document.selection.getSelectionAsText();
console.log('Текст:', text);
})
},
{
id: 'plugin:get-text-html',
title: 'Get as HTML',
type: 'button',
onClick: editorApi.createCallback(async () => {
const html = await editorApi.document.selection.getSelectionAsText('html');
console.log('HTML:', html);
})
}
]
};
Отключаемая кнопка с выпадающим меню
const saveDropdown: ButtonWithDropdown = {
id: 'plugin:save-options',
title: 'Сохранить',
icon: 'Save',
type: 'button-with-dropdown',
disabled: false, // Может быть отключена
onClick: editorApi.createCallback(async () => {
// быстрое сохранение
editorApi.ui.toasts.showToast({ id, content: 'Сохранено' });
}),
// При этом выпадающее меню остается доступным
items: [
{
id: 'plugin:save-as',
title: 'Сохранить как...',
type: 'button',
onClick: editorApi.createCallback(async () => {
// сохранить как
})
},
{
id: 'plugin:save-all',
title: 'Сохранить всё',
type: 'button',
onClick: editorApi.createCallback(async () => {
// сохранить всё
})
}
]
};
Использование в панели инструментов
editorApi.ui.ribbon.addTab({
id: 'plugin:ribbon:tab',
title: 'My Plugin',
order: 27,
groups: [
{
id: 'plugin:group:export',
type: 'controls',
controls: [
{
id: 'plugin:export-dropdown',
title: 'Export',
icon: 'Download',
type: 'button-with-dropdown',
groupingId: 'export',
items: [
{
id: 'plugin:export-pdf',
title: 'Export to PDF',
type: 'button',
onClick: editorApi.createCallback(async () => {
// экспорт
})
},
{
id: 'plugin:export-html',
title: 'Export to HTML',
type: 'button',
onClick: editorApi.createCallback(async () => {
// экспорт
})
}
]
}
]
}
]
});
Кнопка с выпадающим меню и вариантами стиля
const actionDropdown: ButtonWithDropdown = {
id: 'plugin:action',
title: 'Действие',
icon: 'Settings',
type: 'button-with-dropdown',
onClick: editorApi.createCallback(() => {
editorApi.ui.toasts.showToast({ id, content: 'Выполнено основное действие' });
}),
items: [
{
id: 'plugin:action-1',
title: 'Вариант 1',
type: 'button',
variant: 'primary',
onClick: editorApi.createCallback(() => {
editorApi.ui.toasts.showToast({ id, content: 'Вариант 1' });
})
},
{
id: 'plugin:action-2',
title: 'Вариант 2',
type: 'button',
variant: 'secondary',
onClick: editorApi.createCallback(() => {
editorApi.ui.toasts.showToast({ id, content: 'Вариант 2' });
})
}
]
};
Рекомендации дизайна:
- Используйте для группировки 2-5 связанных действий
- Первое действие в меню должно быть типичным выбором
- Если есть onClick, первое действие в меню может быть дублем основного действия
- Используйте понятные иконки и названия
- Группируйте по смыслу (экспорт, вставка, форматирование)
See
- BaseControl — базовые свойства (id, title, order, groupingId)
- MayHaveIcon — иконка для этого компонента
- MayBeDisabled — состояние disabled
- Button — элементы в выпадающем меню
- Callback — тип обработчика события
- RibbonApi — использование в панели инструментов
Extends
Properties
checked?
readonlyoptionalchecked:boolean
Состояние компонента: включено (true) или выключено (false).
Remarks
true — компонент в состоянии "включено":
- Галочка в флажке
- Переключатель в "ВКЛ" позиции
- Радиокнопка заполнена
- Визуально выглядит активным
false — компонент в состоянии "выключено":
- Флажок пуст
- Переключатель в "ВЫКЛ" позиции
- Радиокнопка не заполнена
- Визуально выглядит неактивным
undefined — состояние не определено:
- Используется редко
- Может означать неопределённое состояние (не да, не нет)
Example
Флажок (checkbox)
{
id: 'checkbox:confirm',
type: 'checkbox',
title: 'Я согласен с условиями',
checked: false // Изначально не отмечено
}
Переключатель (toggle)
{
id: 'toggle:darkmode',
type: 'toggle',
title: 'Тёмный режим',
checked: true // Изначально включено
}
Обновление состояния
editorApi.ui.updateUiNodes([{
id: 'checkbox:confirm',
checked: true // Отметить флажок
}]);
Inherited from
disabled?
readonlyoptionaldisabled:boolean
Флаг отключения элемента.
Remarks
true — элемент отключен:
- Визуально затемнен/затенён
- События клика/ввода игнорируются
- Подсказка может объяснить причину отключения
- Фокус не может перейти на отключённый элемент
false или не указано — элемент включен:
- Элемент активен и реагирует на действия
- Полная интерактивность
- Нормальный визуальный вид
Default
false (включено)
Example
Отключение кнопки во время загрузки
// Начало загрузки
editorApi.ui.updateUiNodes([{
id: 'button:submit',
disabled: true,
title: 'Загрузка...'
}]);
// После загрузки
editorApi.ui.updateUiNodes([{
id: 'button:submit',
disabled: false,
title: 'Отправить'
}]);
Отключение поля ввода в зависимости от условия
editorApi.events.subscribe('documentChange', (payload) => {
const isEmpty = payload.info.content.length === 0;
editorApi.ui.updateUiNodes([{
id: 'input:search',
disabled: isEmpty // Отключить если документ пуст
}]);
});
Inherited from
groupingId?
readonlyoptionalgroupingId:string
Идентификатор группы, к которой относится элемент.
Remarks
Поведение:
- Элементы с одинаковым groupingId визуально группируются
- Между группами (разные groupingId) отображается разделитель
- Элементы без groupingId не группируются
- Порядок групп определяется order первого элемента в группе
Example
[
{ id: 'copy', title: 'Copy', groupingId: 'clipboard' },
{ id: 'cut', title: 'Cut', groupingId: 'clipboard' },
{ id: 'paste', title: 'Paste', groupingId: 'clipboard' },
// Разделитель добавляется автоматически
{ id: 'delete', title: 'Delete', groupingId: 'edit' }
]
Inherited from
icon?
readonlyoptionalicon:string
Название встроенной иконки или SVG код пользовательской иконки.
Remarks
Если iconType === 'standard':
- Должно быть названием встроенной иконки редактора
- Примеры: 'Save', 'Delete', 'Settings', 'Help', 'Search', 'Download', 'Upload'
- Полный список доступных иконок см. в документации редактора
- Название чувствительно к регистру
Если iconType === 'svg':
- Должно содержать валидный SVG код
- Должен быть корректно оформлен XML
- Может содержать только безопасные SVG элементы
- Рекомендуется использовать viewBox для масштабируемости
Examples
{
icon: 'Save',
iconType: 'standard' // или не указывать, это значение по умолчанию
}
{
icon: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
'<circle cx="12" cy="12" r="10" fill="currentColor"/>' +
'</svg>',
iconType: 'svg'
}
const customIconSvg = `
<svg viewBox="0 0 24 24">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"
fill="currentColor"/>
</svg>
`;
{
icon: customIconSvg,
iconType: 'svg'
}
See
iconType — тип иконки (standard или svg)
Inherited from
iconType?
readonlyoptionaliconType:"standard"|"svg"
Тип иконки, определяющий как интерпретировать поле icon.
Remarks
'standard' — встроенная иконка редактора:
- Быстрая загрузка (уже в памяти)
- Соответствует стилю редактора
- Автоматически масштабируется и окрашивается
- Поддерживает темы (светлая/тёмная)
- Ограниченный выбор (только встроенные иконки)
'svg' — пользовательская SVG иконка:
- Полная гибкость в дизайне
- Можно использовать любую иконку
- Меньше файлов (встроены в код)
- Требует корректного SVG кода
- Поддерживает CSS свойство currentColor для окраски
Default
'standard'
Example
Встроенная иконка (по умолчанию)
{
icon: 'Settings'
// iconType: 'standard' используется по умолчанию
}
SVG иконка
{
icon: '<svg>...</svg>',
iconType: 'svg'
}
Рекомендуемая SVG структура
{
icon: `
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<!-- viewBox позволяет масштабировать без потери качества -->
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"
fill="currentColor" />
<!-- currentColor наследует цвет из контекста -->
</svg>
`,
iconType: 'svg'
}
See
icon — содержимое иконки
Inherited from
id
readonlyid:string
Уникальный идентификатор элемента пользовательского интерфейса.
Remarks
Требования:
- Должен быть уникален среди всех элементов UI редактора
- Не может быть пустой строкой
- Не может содержать спецсимволы (кроме :, -, _)
- Чувствителен к регистру (case-sensitive)
Использование:
- Идентификация элемента при обновлении
- Идентификация элемента при событиях
- Ссылка на элемент в других местах кода
Соглашения:
'plugin:button:save' // Кнопка надстройки
'plugin:input:username' // Поле ввода
'plugin:panel:settings' // Панель параметров
'plugin:group:formatting' // Группа элементов
Example
// Создание элемента с уникальным id
{
id: 'plugin:button:submit',
title: 'Отправить',
type: 'button'
}
// Использование id для обновления
editorApi.ui.updateUiNodes([
{
id: 'plugin:button:submit',
disabled: false,
}
]);
// Использование id для удаления
editorApi.ui.ribbon.removeTabs(['plugin:ribbon:tab']);
Inherited from
items
readonlyitems:Button[]
Массив кнопок, отображаемых в выпадающем меню.
Содержит список элементов Button, которые будут доступны при открытии выпадающего меню.
Remarks
Характеристики items:
- Минимум 1 кнопка в массиве
- Максимум рекомендуется 5-7 кнопок (для удобства)
- Все элементы должны быть типа Button
- Кнопки отображаются в том же порядке, что в массиве
- Каждая кнопка должна иметь свой onClick
- Кнопки могут быть отключены через
disabled
Рекомендации:
- Первая кнопка — наиболее типичный выбор
- Группируйте похожие действия рядом
- Используйте понятные названия и иконки
- Если много действий, подумайте о подменю
Структура Button в items:
- id — уникальный идентификатор
- title — текст кнопки
- type: 'button' — обязательно 'button'
- onClick — обработчик события
- icon (опционально) — иконка
- disabled (опционально) — отключение кнопки
Example
Простой выпадающий список
const dropdown: ButtonWithDropdown = {
id: 'plugin:format',
title: 'Format',
icon: 'Palette',
type: 'button-with-dropdown',
items: [
{
id: 'plugin:format-bold',
title: 'Bold',
type: 'button',
onClick: editorApi.createCallback(() => {})
},
{
id: 'plugin:format-italic',
title: 'Italic',
type: 'button',
onClick: editorApi.createCallback(() => {})
},
{
id: 'plugin:format-underline',
title: 'Underline',
type: 'button',
onClick: editorApi.createCallback(() => {})
}
]
};
Выпадающий список с иконками
const dropdown: ButtonWithDropdown = {
id: 'plugin:align',
title: 'Align',
icon: 'AlignLeft',
type: 'button-with-dropdown',
items: [
{
id: 'plugin:align-left',
title: 'Align Left',
icon: 'AlignLeft',
type: 'button',
onClick: editorApi.createCallback(() => {})
},
{
id: 'plugin:align-center',
title: 'Align Center',
icon: 'AlignCenter',
type: 'button',
onClick: editorApi.createCallback(() => {})
},
{
id: 'plugin:align-right',
title: 'Align Right',
icon: 'AlignRight',
type: 'button',
onClick: editorApi.createCallback(() => {})
}
]
};
Выпадающий список с некоторыми отключенными кнопками
const dropdown: ButtonWithDropdown = {
id: 'plugin:options',
title: 'Options',
icon: 'Settings',
type: 'button-with-dropdown',
items: [
{
id: 'plugin:option-1',
title: 'Option 1',
type: 'button',
onClick: editorApi.createCallback(() => {})
},
{
id: 'plugin:option-2',
title: 'Option 2',
type: 'button',
disabled: true, // Отключена - недоступна
onClick: editorApi.createCallback(() => {})
},
{
id: 'plugin:option-3',
title: 'Option 3',
type: 'button',
onClick: editorApi.createCallback(() => {})
}
]
};
See
onClick?
readonlyoptionalonClick:Callback
Обработчик события для непосредственного взаимодействия с основной частью кнопки.
Опциональный обработчик, который определяет поведение при клике на основную кнопку.
Remarks
Режимы работы:
1. Без onClick (только выпадающее меню):
- Клик на кнопку открывает/закрывает выпадающее меню
- Клик на стрелку также открывает/закрывает меню
- Нет дополнительного действия при клике на основную область
- Используется для простого выбора из списка
2. С onClick (двухфункциональная кнопка):
- Клик на основную часть выполняет onClick
- Клик на стрелку открывает/закрывает меню
- Кнопка имеет две разные функции
- Используется когда есть основное действие + варианты
Поведение:
- Если не указан, кнопка работает только как меню
- При клике на основную область выполняется onClick (если указан)
- При клике на стрелку открывается меню независимо от onClick
- Обработчик может быть синхронным или асинхронным
Default
undefined (режим: только меню)
Example
Только выпадающее меню (без onClick)
const dropdown: ButtonWithDropdown = {
id: 'plugin:export',
title: 'Export',
icon: 'Download',
type: 'button-with-dropdown',
// onClick не указан - кнопка только открывает меню
items: [
{
id: 'plugin:export-pdf',
title: 'PDF',
type: 'button',
onClick: editorApi.createCallback(() => {})
}
]
};
Двухфункциональная кнопка (с onClick)
const dropdown: ButtonWithDropdown = {
id: 'plugin:paste',
title: 'Paste',
icon: 'Paste',
type: 'button-with-dropdown',
// onClick указан - клик на кнопку вставляет, клик на стрелку открывает варианты
onClick: editorApi.createCallback(async () => {
await editorApi.document.clipboard.paste();
}),
items: [
{
id: 'plugin:paste-special',
title: 'Paste Special',
type: 'button',
onClick: editorApi.createCallback(async () => {
// специальная вставка
})
}
]
};
Асинхронный onClick
const dropdown: ButtonWithDropdown = {
id: 'plugin:save',
title: 'Save',
icon: 'Save',
type: 'button-with-dropdown',
onClick: editorApi.createCallback(async () => {
editorApi.ui.toasts.showToast({ id, content: 'Saving...' });
await saveDocument();
editorApi.ui.toasts.showToast({ id, content: 'Saved' });
}),
items: [
{
id: 'plugin:save-as',
title: 'Save As...',
type: 'button',
onClick: editorApi.createCallback(() => {})
}
]
};
See
- Callback — полное описание интерфейса Callback
- EditorApi.createCallback — как создать callback
title
readonlytitle:string
Название/заголовок элемента пользовательского интерфейса.
Remarks
Требования:
- Должен быть локализирован (переведён на язык пользователя)
- Должен быть понятным и кратким
- Не должен быть пустой строкой
- Может содержать спецсимволы (кроме < и >)
Примеры:
- "Сохранить документ"
- "Экспортировать в PDF"
- "Параметры надстройки"
- "Об приложении"
Советы:
- Используйте глаголы для действий (Сохранить, Удалить)
- Используйте существительные для панелей (Параметры, Информация)
- Сохраняйте названия короткими
- Будьте конкретными (не просто "Опции", а "Параметры надстройки")
Example
{
id: 'button:save',
title: 'Сохранить',
type: 'button'
}
Inherited from
type
readonlytype:"button-with-dropdown"
Маркер типа элемента управления.
Используется для идентификации элемента как кнопки с выпадающим меню в контейнере.
Remarks
- Всегда равен
'button-with-dropdown'для этого интерфейса - Используется контейнерами для определения типа элемента
- Позволяет TypeScript правильно типизировать элемент
Default
'button-with-dropdown'
Example
const dropdown: ButtonWithDropdown = {
id: 'plugin:dropdown',
title: 'Действие',
icon: 'Menu',
type: 'button-with-dropdown', // Маркер типа
items: []
};