Improve tooltips

This commit is contained in:
IRBorisov 2024-03-09 16:40:10 +03:00
parent 5b1fe5527f
commit 5e91eccd68
18 changed files with 120 additions and 91 deletions

View File

@ -1,16 +1,15 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { globalIDs } from '@/utils/constants';
interface NavigationButtonProps {
interface NavigationButtonProps extends CProps.Titled {
text?: string;
icon: React.ReactNode;
title?: string;
titleHtml?: string;
onClick?: () => void;
}
function NavigationButton({ icon, title, titleHtml, onClick, text }: NavigationButtonProps) {
function NavigationButton({ icon, title, titleHtml, hideTitle, onClick, text }: NavigationButtonProps) {
return (
<button
type='button'
@ -18,6 +17,7 @@ function NavigationButton({ icon, title, titleHtml, onClick, text }: NavigationB
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
data-tooltip-html={titleHtml}
data-tooltip-content={title}
data-tooltip-hidden={hideTitle}
onClick={onClick}
className={clsx(
'mr-1 h-full', // prettier: split lines

View File

@ -2,8 +2,13 @@
import { HTMLMotionProps } from 'framer-motion';
export namespace CProps {
export type Control = {
export type Titled = {
title?: string;
titleHtml?: string;
hideTitle?: boolean;
};
export type Control = Titled & {
disabled?: boolean;
noBorder?: boolean;
noOutline?: boolean;
@ -23,20 +28,19 @@ export namespace CProps {
};
export type Div = React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
export type Button = Omit<
React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>,
'children' | 'type'
>;
export type Button = Titled &
Omit<
React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>,
'children' | 'type'
>;
export type Label = Omit<
React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>,
'children'
>;
export type TextArea = React.DetailedHTMLProps<
React.TextareaHTMLAttributes<HTMLTextAreaElement>,
HTMLTextAreaElement
>;
export type Input = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
export type TextArea = Titled &
React.DetailedHTMLProps<React.TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>;
export type Input = Titled & React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
export type AnimatedButton = Omit<HTMLMotionProps<'button'>, 'type'>;
export type AnimatedButton = Titled & Omit<HTMLMotionProps<'button'>, 'type'>;
export type AnimatedDiv = HTMLMotionProps<'div'>;
}

View File

@ -7,10 +7,8 @@ import { CProps } from '../props';
interface ButtonProps extends CProps.Control, CProps.Colors, CProps.Button {
text?: string;
icon?: React.ReactNode;
titleHtml?: string;
dense?: boolean;
hideTitle?: boolean;
loading?: boolean;
}
@ -19,10 +17,10 @@ function Button({
icon,
title,
titleHtml,
hideTitle,
loading,
dense,
disabled,
hideTitle,
noBorder,
noOutline,
colors = 'clr-btn-default',

View File

@ -8,14 +8,24 @@ import { CProps } from '../props';
export interface CheckboxProps extends Omit<CProps.Button, 'value' | 'onClick'> {
label?: string;
titleHtml?: string;
disabled?: boolean;
value: boolean;
setValue?: (newValue: boolean) => void;
}
function Checkbox({ id, disabled, label, title, titleHtml, className, value, setValue, ...restProps }: CheckboxProps) {
function Checkbox({
id,
disabled,
label,
title,
titleHtml,
hideTitle,
className,
value,
setValue,
...restProps
}: CheckboxProps) {
const cursor = useMemo(() => {
if (disabled) {
return 'cursor-not-allowed';
@ -50,6 +60,7 @@ function Checkbox({ id, disabled, label, title, titleHtml, className, value, set
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
data-tooltip-html={titleHtml}
data-tooltip-content={title}
data-tooltip-hidden={hideTitle}
{...restProps}
>
<div

View File

@ -7,7 +7,6 @@ import { CheckboxCheckedIcon, CheckboxNullIcon } from '../Icons';
import { CheckboxProps } from './Checkbox';
export interface CheckboxTristateProps extends Omit<CheckboxProps, 'value' | 'setValue'> {
titleHtml?: string;
value: boolean | null;
setValue?: (newValue: boolean | null) => void;
}
@ -18,6 +17,7 @@ function CheckboxTristate({
label,
title,
titleHtml,
hideTitle,
className,
value,
setValue,
@ -62,6 +62,7 @@ function CheckboxTristate({
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
data-tooltip-html={titleHtml}
data-tooltip-content={title}
data-tooltip-hidden={hideTitle}
{...restProps}
>
<div

View File

@ -8,7 +8,6 @@ import { CProps } from '../props';
interface DropdownButtonProps extends CProps.AnimatedButton {
text?: string;
titleHtml?: string;
icon?: React.ReactNode;
children?: React.ReactNode;
@ -20,6 +19,7 @@ function DropdownButton({
className,
title,
titleHtml,
hideTitle,
onClick,
children,
...restProps
@ -43,6 +43,7 @@ function DropdownButton({
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
data-tooltip-html={titleHtml}
data-tooltip-content={title}
data-tooltip-hidden={hideTitle}
{...restProps}
>
{children ? children : null}

View File

@ -6,18 +6,16 @@ import { CProps } from '../props';
interface MiniButtonProps extends CProps.Button {
icon: React.ReactNode;
titleHtml?: string;
noHover?: boolean;
hideTitle?: boolean;
}
function MiniButton({
icon,
noHover,
hideTitle,
tabIndex,
title,
titleHtml,
hideTitle,
className,
...restProps
}: MiniButtonProps) {

View File

@ -7,6 +7,7 @@ import { BiX } from 'react-icons/bi';
import useEscapeKey from '@/hooks/useEscapeKey';
import { animateModal } from '@/styling/animations';
import { prepareTooltip } from '@/utils/labels';
import { CProps } from '../props';
import Button from './Button';
@ -71,7 +72,11 @@ function Modal({
{...restProps}
>
<Overlay position='right-[0.3rem] top-2'>
<MiniButton title='Закрыть диалоговое окно [ESC]' icon={<BiX size='1.25rem' />} onClick={handleCancel} />
<MiniButton
titleHtml={prepareTooltip('Закрыть диалоговое окно', 'ESC')}
icon={<BiX size='1.25rem' />}
onClick={handleCancel}
/>
</Overlay>
{header ? <h1 className='px-12 py-2 select-none'>{header}</h1> : null}

View File

@ -6,12 +6,10 @@ import { CProps } from '../props';
interface SelectorButtonProps extends CProps.Button {
text?: string;
titleHtml?: string;
icon?: React.ReactNode;
colors?: string;
transparent?: boolean;
hideTitle?: boolean;
}
function SelectorButton({

View File

@ -4,12 +4,13 @@ import { Tab as TabImpl } from 'react-tabs';
import { globalIDs } from '@/utils/constants';
interface TabLabelProps extends Omit<TabPropsImpl, 'children'> {
import { CProps } from '../props';
interface TabLabelProps extends Omit<TabPropsImpl, 'children'>, CProps.Titled {
label?: string;
titleHtml?: string;
}
function TabLabel({ label, title, titleHtml, className, ...otherProps }: TabLabelProps) {
function TabLabel({ label, title, titleHtml, hideTitle, className, ...otherProps }: TabLabelProps) {
return (
<TabImpl
className={clsx(
@ -23,6 +24,7 @@ function TabLabel({ label, title, titleHtml, className, ...otherProps }: TabLabe
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
data-tooltip-html={titleHtml}
data-tooltip-content={title}
data-tooltip-hidden={hideTitle}
{...otherProps}
>
{label}

View File

@ -6,6 +6,7 @@ import { FiSave } from 'react-icons/fi';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
import { prepareTooltip } from '@/utils/labels';
interface ConstituentaToolbarProps {
isMutable: boolean;
@ -36,7 +37,7 @@ function ConstituentaToolbar({
return (
<Overlay position='top-1 right-4 sm:right-1/2 sm:translate-x-1/2' className='flex'>
<MiniButton
title='Сохранить изменения [Ctrl + S]'
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
disabled={!canSave}
icon={<FiSave size='1.25rem' className='icon-primary' />}
onClick={onSubmit}
@ -54,7 +55,7 @@ function ConstituentaToolbar({
icon={<BiPlusCircle size={'1.25rem'} className='icon-green' />}
/>
<MiniButton
title='Клонировать конституенту [Alt + V]'
titleHtml={prepareTooltip('Клонировать конституенту', 'Alt + V]')}
disabled={!isMutable || isModified}
onClick={onClone}
icon={<BiDuplicate size='1.25rem' className='icon-green' />}
@ -66,13 +67,13 @@ function ConstituentaToolbar({
icon={<BiTrash size='1.25rem' className='icon-red' />}
/>
<MiniButton
title='Переместить вверх [Alt + вверх]'
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
icon={<BiUpvote size='1.25rem' className='icon-primary' />}
disabled={!isMutable}
onClick={onMoveUp}
/>
<MiniButton
title='Переместить вниз [Alt + вниз]'
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
icon={<BiDownvote size='1.25rem' className='icon-primary' />}
disabled={!isMutable}
onClick={onMoveDown}

View File

@ -1,23 +1,25 @@
import clsx from 'clsx';
import { CProps } from '@/components/props';
import { TokenID } from '@/models/rslang';
import { globalIDs } from '@/utils/constants';
interface RSLocalButtonProps {
interface RSLocalButtonProps extends CProps.Titled {
text: string;
title: string;
disabled?: boolean;
onInsert: (token: TokenID, key?: string) => void;
}
function RSLocalButton({ text, title, disabled, onInsert }: RSLocalButtonProps) {
function RSLocalButton({ text, title, titleHtml, hideTitle, disabled, onInsert }: RSLocalButtonProps) {
return (
<button
type='button'
tabIndex={-1}
disabled={disabled}
data-tooltip-id={title ? globalIDs.tooltip : undefined}
data-tooltip-id={!!title || !!titleHtml ? globalIDs.tooltip : undefined}
data-tooltip-html={titleHtml}
data-tooltip-content={title}
data-tooltip-hidden={hideTitle}
className={clsx(
'w-[1.7rem] sm:w-[2rem] h-5 sm:h-6',
'cursor-pointer disabled:cursor-default',

View File

@ -31,7 +31,7 @@ function RSTokenButton({ token, disabled, onInsert }: RSTokenButtonProps) {
}
)}
data-tooltip-id={globalIDs.tooltip}
data-tooltip-content={describeToken(token)}
data-tooltip-html={describeToken(token)}
>
{label ? <span className='whitespace-nowrap'>{label}</span> : null}
</button>

View File

@ -12,7 +12,7 @@ import { inferStatus } from '@/models/rsformAPI';
import { IExpressionParse, ParsingStatus } from '@/models/rslang';
import { colorBgCstStatus } from '@/styling/color';
import { globalIDs } from '@/utils/constants';
import { labelExpressionStatus } from '@/utils/labels';
import { labelExpressionStatus, prepareTooltip } from '@/utils/labels';
import StatusIcon from './StatusIcon';
@ -49,7 +49,7 @@ function StatusBar({ isModified, processing, constituenta, parseData, onAnalyze
)}
style={{ backgroundColor: processing ? colors.bgDefault : colorBgCstStatus(status, colors) }}
data-tooltip-id={globalIDs.tooltip}
data-tooltip-content='Проверить определение [Ctrl + Q]'
data-tooltip-html={prepareTooltip('Проверить определение', 'Ctrl + Q')}
onClick={onAnalyze}
>
<AnimatePresence mode='wait'>

View File

@ -9,6 +9,7 @@ import HelpButton from '@/components/Help/HelpButton';
import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
import { HelpTopic } from '@/models/miscellaneous';
import { prepareTooltip } from '@/utils/labels';
import { useRSEdit } from '../RSEditContext';
@ -28,7 +29,7 @@ function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, o
<Overlay position='top-1 right-1/2 translate-x-1/2' className='flex'>
{controller.isContentEditable || controller.isProcessing ? (
<MiniButton
title='Сохранить изменения [Ctrl + S]'
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
disabled={!canSave}
icon={<FiSave size='1.25rem' className='icon-primary' />}
onClick={onSubmit}
@ -46,7 +47,7 @@ function RSFormToolbar({ modified, anonymous, subscribed, claimable, onSubmit, o
/>
{!anonymous ? (
<MiniButton
title={`Отслеживание ${subscribed ? 'включено' : 'выключено'}`}
titleHtml={`Отслеживание <b>${subscribed ? 'включено' : 'выключено'}</b>`}
disabled={controller.isProcessing}
icon={
subscribed ? (

View File

@ -13,7 +13,7 @@ import { HelpTopic } from '@/models/miscellaneous';
import { CstType } from '@/models/rsform';
import { getCstTypePrefix } from '@/models/rsformAPI';
import { prefixes } from '@/utils/constants';
import { getCstTypeShortcut, labelCstType } from '@/utils/labels';
import { getCstTypeShortcut, labelCstType, prepareTooltip } from '@/utils/labels';
import { useRSEdit } from '../RSEditContext';
@ -29,25 +29,25 @@ function RSListToolbar({ selectedCount }: RSListToolbarProps) {
return (
<Overlay position='top-1 right-1/2 translate-x-1/2' className='flex items-start'>
<MiniButton
title='Переместить вверх [Alt + вверх]'
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
icon={<BiUpvote size='1.25rem' className='icon-primary' />}
disabled={!controller.isMutable || nothingSelected}
onClick={controller.moveUp}
/>
<MiniButton
title='Переместить вниз [Alt + вниз]'
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
icon={<BiDownvote size='1.25rem' className='icon-primary' />}
disabled={!controller.isMutable || nothingSelected}
onClick={controller.moveDown}
/>
<MiniButton
title='Клонировать конституенту [Alt + V]'
titleHtml={prepareTooltip('Клонировать конституенту', 'Alt + V')}
icon={<BiDuplicate size='1.25rem' className='icon-green' />}
disabled={!controller.isMutable || selectedCount !== 1}
onClick={controller.cloneCst}
/>
<MiniButton
title='Добавить новую конституенту... [Alt + `]'
titleHtml={prepareTooltip('Добавить новую конституенту...', 'Alt + `')}
icon={<BiPlusCircle size='1.25rem' className='icon-green' />}
disabled={!controller.isMutable}
onClick={() => controller.createCst(undefined, false)}
@ -66,13 +66,13 @@ function RSListToolbar({ selectedCount }: RSListToolbarProps) {
key={`${prefixes.csttype_list}${typeStr}`}
text={`${getCstTypePrefix(typeStr as CstType)}1 — ${labelCstType(typeStr as CstType)}`}
onClick={() => controller.createCst(typeStr as CstType, true)}
title={getCstTypeShortcut(typeStr as CstType)}
titleHtml={getCstTypeShortcut(typeStr as CstType)}
/>
))}
</Dropdown>
</div>
<MiniButton
title='Удалить выбранные [Delete]'
titleHtml={prepareTooltip('Удалить выбранные', 'Delete')}
icon={<BiTrash size='1.25rem' className='icon-red' />}
disabled={!controller.isMutable || nothingSelected}
onClick={controller.deleteCst}

View File

@ -38,7 +38,7 @@ function UserTabs() {
<div>
<Overlay position='top-0 right-0'>
<MiniButton
title='Показать/Скрыть отслеживаемые схемы'
title='Отслеживаемые схемы'
icon={
showSubs ? (
<FiBell size='1.25rem' className='icon-primary' />

View File

@ -136,50 +136,50 @@ export function getCstTypeShortcut(type: CstType) {
}
}
/**
/** <b></b><br/>
* Generates description for {@link TokenID}.
*/
export function describeToken(id: TokenID): string {
// prettier-ignore
switch (id) {
case TokenID.BOOLEAN: return 'Булеан [Alt + E / Shift + B]';
case TokenID.DECART: return 'Декартово произведение [Alt + Shift + E / Shift + 8]';
case TokenID.PUNCTUATION_PL: return 'Скобки вокруг выражения [Alt + Shift + 9 ]';
case TokenID.PUNCTUATION_SL: return 'Скобки вокруг выражения [Alt + [ ]';
case TokenID.QUANTOR_UNIVERSAL: return 'Квантор всеобщности [`]';
case TokenID.QUANTOR_EXISTS: return 'Квантор существования [Shift + `]';
case TokenID.LOGIC_NOT: return 'Отрицание [Alt + `]';
case TokenID.LOGIC_AND: return 'Конъюнкция [Alt + 3 ~ Shift + 7]';
case TokenID.LOGIC_OR: return 'Дизъюнкция [Alt + Shift + 3]';
case TokenID.LOGIC_IMPLICATION: return 'Импликация [Alt + 4]';
case TokenID.LOGIC_EQUIVALENT: return 'Эквивалентность [Alt + Shift + 4]';
case TokenID.LIT_EMPTYSET: return 'Пустое множество [Alt + X]';
case TokenID.LIT_WHOLE_NUMBERS: return 'Целые числа [Alt + Z]';
case TokenID.EQUAL: return 'Равенство';
case TokenID.NOTEQUAL: return 'Неравенство [Alt + Shift + `]';
case TokenID.GREATER_OR_EQ: return 'Больше или равно [Alt + Shift + 7]';
case TokenID.LESSER_OR_EQ: return 'Меньше или равно [Alt + Shift + 8]';
case TokenID.SET_IN: return 'Быть элементом (принадлежит) [Alt + 1]';
case TokenID.SET_NOT_IN: return 'Не принадлежит [Alt + Shift + 1]';
case TokenID.SUBSET_OR_EQ: return 'Быть частью (нестрогое подмножество) [Alt + 2]';
case TokenID.SUBSET: return 'Строгое подмножество [Alt + 7]';
case TokenID.NOT_SUBSET: return 'Не подмножество [Alt + Shift + 2]';
case TokenID.SET_INTERSECTION: return 'Пересечение [Alt + A]';
case TokenID.SET_UNION: return 'Объединение [Alt + S]';
case TokenID.SET_MINUS: return 'Разность множеств [Alt + 5]';
case TokenID.SET_SYMMETRIC_MINUS: return 'Симметрическая разность [Alt + Shift + 5]';
case TokenID.NT_DECLARATIVE_EXPR: return 'Декларативная форма определения терма [Alt + D]';
case TokenID.NT_IMPERATIVE_EXPR: return 'Императивная форма определения терма [Alt + G]';
case TokenID.NT_RECURSIVE_FULL: return 'Рекурсивная (цикличная) форма определения терма [Alt + T]';
case TokenID.BIGPR: return 'Большая проекция [Alt + Q]';
case TokenID.SMALLPR: return 'Малая проекция [Alt + W]';
case TokenID.FILTER: return 'Фильтр [Alt + F]';
case TokenID.REDUCE: return 'Множество-сумма [Alt + R]';
case TokenID.CARD: return 'Мощность [Alt + C]';
case TokenID.BOOL: return 'Синглетон [Alt + B]';
case TokenID.DEBOOL: return 'Десинглетон [Alt + V]';
case TokenID.PUNCTUATION_ASSIGN: return 'Присвоение (императивный синтаксис) [Alt + Shift + 6]';
case TokenID.PUNCTUATION_ITERATE: return 'Перебор элементов множества (императивный синтаксис) [Alt + 6]';
case TokenID.BOOLEAN: return prepareTooltip('Булеан', 'Alt + E / Shift + B');
case TokenID.DECART: return prepareTooltip('Декартово произведение', 'Alt + Shift + E / Shift + 8');
case TokenID.PUNCTUATION_PL: return prepareTooltip('Скобки () вокруг выражения', 'Alt + Shift + 9');
case TokenID.PUNCTUATION_SL: return prepareTooltip('Скобки [] вокруг выражения', 'Alt + [');
case TokenID.QUANTOR_UNIVERSAL: return prepareTooltip('Квантор всеобщности', '`');
case TokenID.QUANTOR_EXISTS: return prepareTooltip('Квантор существования', 'Shift + `');
case TokenID.LOGIC_NOT: return prepareTooltip('Отрицание', 'Alt + `');
case TokenID.LOGIC_AND: return prepareTooltip('Конъюнкция', 'Alt + 3 ~ Shift + 7');
case TokenID.LOGIC_OR: return prepareTooltip('Дизъюнкция', 'Alt + Shift + 3');
case TokenID.LOGIC_IMPLICATION: return prepareTooltip('Импликация', 'Alt + 4');
case TokenID.LOGIC_EQUIVALENT: return prepareTooltip('Эквивалентность', 'Alt + Shift + 4');
case TokenID.LIT_EMPTYSET: return prepareTooltip('Пустое множество', 'Alt + X');
case TokenID.LIT_WHOLE_NUMBERS: return prepareTooltip('Целые числа', 'Alt + Z');
case TokenID.EQUAL: return prepareTooltip('Равенство');
case TokenID.NOTEQUAL: return prepareTooltip('Неравенство', 'Alt + Shift + `');
case TokenID.GREATER_OR_EQ: return prepareTooltip('Больше или равно', 'Alt + Shift + 7');
case TokenID.LESSER_OR_EQ: return prepareTooltip('Меньше или равно', 'Alt + Shift + 8');
case TokenID.SET_IN: return prepareTooltip('Быть элементом (принадлежит)', 'Alt + 1');
case TokenID.SET_NOT_IN: return prepareTooltip('Не принадлежит', 'Alt + Shift + 1');
case TokenID.SUBSET_OR_EQ: return prepareTooltip('Быть частью (нестрогое подмножество)', 'Alt + 2');
case TokenID.SUBSET: return prepareTooltip('Строгое подмножество', 'Alt + 7');
case TokenID.NOT_SUBSET: return prepareTooltip('Не подмножество', 'Alt + Shift + 2');
case TokenID.SET_INTERSECTION: return prepareTooltip('Пересечение', 'Alt + A');
case TokenID.SET_UNION: return prepareTooltip('Объединение', 'Alt + S');
case TokenID.SET_MINUS: return prepareTooltip('Разность множеств', 'Alt + 5');
case TokenID.SET_SYMMETRIC_MINUS: return prepareTooltip('Симметрическая разность', 'Alt + Shift + 5');
case TokenID.NT_DECLARATIVE_EXPR: return prepareTooltip('Декларативное определение', 'Alt + D');
case TokenID.NT_IMPERATIVE_EXPR: return prepareTooltip('Императивное определение', 'Alt + G');
case TokenID.NT_RECURSIVE_FULL: return prepareTooltip('Рекурсивное определение (цикл)', 'Alt + T');
case TokenID.BIGPR: return prepareTooltip('Большая проекция', 'Alt + Q');
case TokenID.SMALLPR: return prepareTooltip('Малая проекция', 'Alt + W');
case TokenID.FILTER: return prepareTooltip('Фильтр', 'Alt + F');
case TokenID.REDUCE: return prepareTooltip('Множество-сумма', 'Alt + R');
case TokenID.CARD: return prepareTooltip('Мощность', 'Alt + C');
case TokenID.BOOL: return prepareTooltip('Синглетон', 'Alt + B');
case TokenID.DEBOOL: return prepareTooltip('Десинглетон', 'Alt + V');
case TokenID.PUNCTUATION_ASSIGN: return prepareTooltip('Присвоение', 'Alt + Shift + 6');
case TokenID.PUNCTUATION_ITERATE: return prepareTooltip('Перебор элементов множества', 'Alt + 6');
}
return `no description: ${id}`;
}
@ -743,3 +743,10 @@ export function describeAccessMode(mode: UserAccessMode): string {
return 'Режим редактирования администратором';
}
}
/**
* Generate HTML wrapper for control description including hotkey.
*/
export function prepareTooltip(text: string, hotkey?: string) {
return hotkey ? `<b>[${hotkey}]</b><br/>${text}` : text;
}