Type Alias: ContextMenuItemWithDropdown
ContextMenuItemWithDropdown =
BaseContextMenuItem&object
Элемент контекстного меню с вложенным выпадающим списком (каскадное меню).
Представляет собой пункт меню, при наведении или клике на который открывается подменю с дополнительными опциями. Используется для организации связанных действий.
Наследует все свойства от BaseContextMenuItem и добавляет массив вложенных элементов items.
Type declaration
items
items:
ContextMenuItem[]
Массив вложенных элементов второго уровня (подменю).
Содержит элементы, которые будут отображены в выпадающем подменю при наведении или клике на родительский пункт.
Remarks
Допустимые типы вложенных элементов:
- SimpleContextMenuItem — простые пункты меню
- ContextMenuItemWithDropdown — вложенные подменю (до 2 уровней)
- ContextMenuItemWithGroup — пункты с groupingId
- ContextMenuItem — объединённый тип всех вышеперечисленных
Ограничения:
- Минимум 1 элемент в массиве
- Максимум 5-7 элементов для удобства
- Максимум 2 уровня вложенности (для UX)
- Элементы могут содержать shownInScopes и disabledInScopes
Порядок элементов:
- Элементы отображаются в том же порядке, что и в массиве
- Порядок можно изменять, переупорядочивая элементы в items
- Пункты с одинаковым groupingId автоматически группируются
Наследование условий видимости:
- Если родительский пункт скрыт (shownInScopes не совпадает), то и подменю скрыто
- Если родительский пункт отключен (disabledInScopes совпадает), то и подменю недоступно
- Вложенные элементы могут иметь свои shownInScopes и disabledInScopes
Производительность:
- Подменю загружается лениво (только при открытии)
- Не влияет на производительность основного меню
- Рекомендуется не использовать более 3 уровней вложенности
Example
Простой массив подменю
const dropdown: ContextMenuItemWithDropdown = {
id: 'plugin:export',
title: 'Экспортировать',
items: [
{
id: 'plugin:export-pdf',
title: 'PDF',
onClick: editorApi.createCallback(() => {})
},
{
id: 'plugin:export-html',
title: 'HTML',
onClick: editorApi.createCallback(() => {})
}
]
};
Подменю с вложенным подменю (2 уровня)
const dropdown: ContextMenuItemWithDropdown = {
id: 'plugin:main',
title: 'Главное меню',
items: [
{
id: 'plugin:action',
title: 'Действие',
onClick: editorApi.createCallback(() => {})
},
{
id: 'plugin:submenu',
title: 'Подменю',
items: [ // Вложенное подменю
{
id: 'plugin:subaction',
title: 'Поддействие',
onClick: editorApi.createCallback(() => {})
}
]
}
]
};
Подменю с условной видимостью
const dropdown: ContextMenuItemWithDropdown = {
id: 'plugin:text-actions',
title: 'Действия с текстом',
shownInScopes: [{ scope: 'text' }], // Видимо только над текстом
items: [
{
id: 'plugin:uppercase',
title: 'ВЕРХНИЙ РЕГИСТР',
onClick: editorApi.createCallback(async () => {
const text = await editorApi.document.selection.getSelectionAsText();
if (text) {
await editorApi.document.insertContent(text.toUpperCase());
}
})
},
{
id: 'plugin:lowercase',
title: 'нижний регистр',
onClick: editorApi.createCallback(async () => {
const text = await editorApi.document.selection.getSelectionAsText();
if (text) {
await editorApi.document.insertContent(text.toLowerCase());
}
})
}
]
};
Подменю с groupingId
const dropdown: ContextMenuItemWithDropdown = {
id: 'plugin:analysis',
title: 'Анализ',
icon: 'BarChart',
items: [
{
id: 'plugin:word-count',
title: 'Количество слов',
groupingId: 'plugin_stats',
onClick: editorApi.createCallback(() => {})
},
{
id: 'plugin:char-count',
title: 'Количество символов',
groupingId: 'plugin_stats',
onClick: editorApi.createCallback(() => {})
},
{
id: 'plugin:readability',
title: 'Читаемость',
groupingId: 'plugin_quality',
onClick: editorApi.createCallback(() => {})
}
]
};
See
- ContextMenuItem — полное описание всех типов элементов
- SimpleContextMenuItem — для простых пунктов
- BaseContextMenuItem.shownInScopes — условная видимость
- BaseContextMenuItem.disabledInScopes — условное отключение
Remarks
Иерархия типов:
ContextMenuItemWithDropdown (текущий тип)
└── extends BaseContextMenuItem
├── extends BaseControl
│ ├── id: string (обязательно)
│ ├── title: string (обязательно)
│ └── ...
└── extends MayHaveIcon
└── icon?: string (опционально)
└── items: ContextMenuItem[] (обязательно)
Ключевые отличия от других типов:
SimpleContextMenuItem— простой пункт без подменюContextMenuItemWithDropdown— пункт с выпадающим подменю (текущий)ContextMenuItemWithGroup— пункт с groupingId для группировки
Структура вложенного подменю:
- Может содержать SimpleContextMenuItem (обычные пункты)
- Может содержать ContextMenuItemWithDropdown (вложенные подменю, до 2 уровней)
- Может содержать ContextMenuItemWithGroup (пункты с groupingId)
- Максимум вложенности: 2 уровня (для удобства)
Когда использовать ContextMenuItemWithDropdown:
- Для группировки связанных действий (Экспорт, Трансформация, Анализ)
- Когда действий много и нужна хорошая организация
- Для создания иерархической структуры меню
- Когда нужна структура "Действие → Способ/Тип"
Поведение при отображении:
- Пункт отображается с указателем (стрелка) обозначающим наличие подменю
- При наведении мышки или клике открывается подменю
- Подменю закрывается при клике вне меню
- На мобильных устройствах: требуется клик для открытия подменю
Рекомендации дизайна:
- Максимум 1-2 уровня вложенности
- Максимум 5-7 пунктов в подменю
- Используйте понятные названия категорий
- Добавляйте иконки для быстрого распознавания
Example
Простое выпадающее меню с 2 пунктами
const dropdownMenu: ContextMenuItemWithDropdown = {
id: 'plugin:text-case',
title: 'Регистр текста',
icon: 'TextCase',
items: [
{
id: 'plugin:uppercase',
title: 'ВЕРХНИЙ РЕГИСТР',
onClick: editorApi.createCallback(async () => {
const text = await editorApi.document.selection.getSelectionAsText();
if (text) {
await editorApi.document.insertContent(text.toUpperCase());
}
})
},
{
id: 'plugin:lowercase',
title: 'нижний регистр',
onClick: editorApi.createCallback(async () => {
const text = await editorApi.document.selection.getSelectionAsText();
if (text) {
await editorApi.document.insertContent(text.toLowerCase());
}
})
}
]
};
editorApi.ui.contextMenu.addItems([dropdownMenu]);
Выпадающее меню "Экспортировать"
const exportMenu: ContextMenuItemWithDropdown = {
id: 'plugin:export',
title: 'Экспортировать',
icon: 'Download',
items: [
{
id: 'plugin:export-pdf',
title: 'Экспортировать в PDF',
onClick: editorApi.createCallback(async () => {
editorApi.ui.toasts.showToast({ id, content: 'Экспорт в PDF...' });
// экспорт в PDF
})
},
{
id: 'plugin:export-html',
title: 'Экспортировать в HTML',
onClick: editorApi.createCallback(async () => {
editorApi.ui.toasts.showToast({ id, content: 'Экспорт в HTML...' });
// экспорт в HTML
})
},
{
id: 'plugin:export-markdown',
title: 'Экспортировать в Markdown',
onClick: editorApi.createCallback(async () => {
editorApi.ui.toasts.showToast({ id, content: 'Экспорт в Markdown...' });
// экспорт в Markdown
})
}
]
};
editorApi.ui.contextMenu.addItems([exportMenu]);
Выпадающее меню с условной видимостью
const conditionalMenu: ContextMenuItemWithDropdown = {
id: 'plugin:text-transform',
title: 'Трансформировать текст',
icon: 'TextTransform',
shownInScopes: [
{ scope: 'text' },
{ scope: 'selection' }
],
items: [
{
id: 'plugin:trim',
title: 'Убрать пробелы',
onClick: editorApi.createCallback(async () => {
const text = await editorApi.document.selection.getSelectionAsText();
if (text) {
await editorApi.document.insertContent(text.trim());
}
})
},
{
id: 'plugin:remove-newlines',
title: 'Убрать переводы строк',
onClick: editorApi.createCallback(async () => {
const text = await editorApi.document.selection.getSelectionAsText();
if (text) {
await editorApi.document.insertContent(text.replace(/\n/g, ' '));
}
})
}
]
};
Выпадающее меню "Вставить"
const insertMenu: ContextMenuItemWithDropdown = {
id: 'plugin:insert',
title: 'Вставить',
icon: 'Plus',
groupingId: 'plugin_insert',
items: [
{
id: 'plugin:insert-date',
title: 'Дату',
onClick: editorApi.createCallback(async () => {
const today = new Date().toLocaleDateString('ru-RU');
await editorApi.document.insertContent(today);
})
},
{
id: 'plugin:insert-time',
title: 'Время',
onClick: editorApi.createCallback(async () => {
const now = new Date().toLocaleTimeString('ru-RU');
await editorApi.document.insertContent(now);
})
},
{
id: 'plugin:insert-timestamp',
title: 'Дату и время',
onClick: editorApi.createCallback(async () => {
const timestamp = new Date().toLocaleString('ru-RU');
await editorApi.document.insertContent(timestamp);
})
}
]
};
Вложенное подменю (2 уровня)
const nestedMenu: ContextMenuItemWithDropdown = {
id: 'plugin:formatting',
title: 'Форматирование',
icon: 'Format',
items: [
{
id: 'plugin:style-bold',
title: 'Жирный',
onClick: editorApi.createCallback(async () => {
await editorApi.document.insertContent('<b>текст</b>', 'html');
})
},
{
id: 'plugin:style-italic',
title: 'Курсив',
onClick: editorApi.createCallback(async () => {
await editorApi.document.insertContent('<i>текст</i>', 'html');
})
},
// Вложенное подменю (до 2-3 уровней)
{
id: 'plugin:colors',
title: 'Цвет текста',
items: [
{
id: 'plugin:color-red',
title: 'Красный',
onClick: editorApi.createCallback(async () => {
await editorApi.document.insertContent(
'<span style="color: red;">текст</span>',
'html'
);
})
},
{
id: 'plugin:color-blue',
title: 'Синий',
onClick: editorApi.createCallback(async () => {
await editorApi.document.insertContent(
'<span style="color: blue;">текст</span>',
'html'
);
})
}
]
}
]
};
Полный пример надстройки с выпадающим меню
export default {
onInit: (editorApi) => {
editorApi.ui.contextMenu.addItems([
{
id: 'plugin:main-menu',
title: 'Моя надстройка',
icon: 'Plugin',
groupingId: 'plugin_main',
items: [
{
id: 'plugin:action-1',
title: 'Действие 1',
onClick: editorApi.createCallback(() => {
console.log('Действие 1');
})
},
{
id: 'plugin:action-2',
title: 'Действие 2',
onClick: editorApi.createCallback(() => {
console.log('Действие 2');
})
},
{
id: 'plugin:submenu',
title: 'Подменю',
items: [
{
id: 'plugin:subaction-1',
title: 'Поддействие 1',
onClick: editorApi.createCallback(() => {
console.log('Поддействие 1');
})
},
{
id: 'plugin:subaction-2',
title: 'Поддействие 2',
onClick: editorApi.createCallback(() => {
console.log('Поддействие 2');
})
}
]
}
]
}
]);
},
onDestroy: (editorApi) => {
editorApi.ui.contextMenu.removeItems([
'plugin:main-menu'
]);
}
}
See
- BaseContextMenuItem — наследуемые свойства
- SimpleContextMenuItem — простой пункт без подменю
- ContextMenuItemWithGroup — для пунктов с группировкой
- ContextMenuItem — объединение всех типов элементов меню
- ContextMenuApi — API управления контекстным меню