@atlaskit/editor-plugin-text-formatting
Version:
Text-formatting plugin for @atlaskit/editor-core
268 lines (267 loc) • 9.46 kB
JavaScript
/**
* @jsxRuntime classic
* @jsx jsx
*/
import { useMemo } from 'react';
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
import { jsx } from '@emotion/react';
import { TOOLBAR_ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
import { getAriaKeyshortcuts, toggleBold, toggleCode, toggleItalic, toggleStrikethrough, toggleSubscript, toggleSuperscript, toggleUnderline, tooltip, ToolTipContent } from '@atlaskit/editor-common/keymaps';
import { toolbarMessages } from '@atlaskit/editor-common/messages';
import { editorCommandToPMCommand } from '@atlaskit/editor-common/preset';
import { shortcutStyle } from '@atlaskit/editor-shared-styles/shortcut';
import AngleBracketsIcon from '@atlaskit/icon/core/angle-brackets';
import BoldIcon from '@atlaskit/icon/core/text-bold';
import ItalicIcon from '@atlaskit/icon/core/text-italic';
import TextStrikethroughIcon from '@atlaskit/icon/core/text-strikethrough';
import UnderlineIcon from '@atlaskit/icon/core/text-underline';
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
import { toggleCodeWithAnalytics, toggleEmWithAnalytics, toggleStrikeWithAnalytics, toggleStrongWithAnalytics, toggleSubscriptWithAnalytics, toggleSuperscriptWithAnalytics, toggleUnderlineWithAnalytics } from '../../../editor-commands/toggle-mark';
import { Subscript, Superscript } from '../icons';
import { getInputMethod } from '../input-method-utils';
import { IconTypes } from '../types';
const withInputMethod = (toolbarType, func) => editorCommandToPMCommand(func(getInputMethod(toolbarType)));
const IconButtons = (editorAnalyticsAPI, toolbarType) => ({
strong: {
buttonId: TOOLBAR_ACTION_SUBJECT_ID.TEXT_FORMATTING_STRONG,
command: withInputMethod(toolbarType, toggleStrongWithAnalytics(editorAnalyticsAPI)),
message: toolbarMessages.bold,
tooltipKeymap: toggleBold,
component: () => jsx(BoldIcon, {
color: "currentColor",
spacing: "spacious",
label: ""
})
},
em: {
buttonId: TOOLBAR_ACTION_SUBJECT_ID.TEXT_FORMATTING_ITALIC,
command: withInputMethod(toolbarType, toggleEmWithAnalytics(editorAnalyticsAPI)),
message: toolbarMessages.italic,
tooltipKeymap: toggleItalic,
component: () => jsx(ItalicIcon, {
color: "currentColor",
spacing: "spacious",
label: ""
})
},
underline: {
buttonId: TOOLBAR_ACTION_SUBJECT_ID.TEXT_FORMATTING_UNDERLINE,
command: withInputMethod(toolbarType, toggleUnderlineWithAnalytics(editorAnalyticsAPI)),
message: toolbarMessages.underline,
tooltipKeymap: toggleUnderline,
component: () => jsx(UnderlineIcon, {
color: "currentColor",
spacing: "spacious",
label: ""
})
},
strike: {
command: withInputMethod(toolbarType, toggleStrikeWithAnalytics(editorAnalyticsAPI)),
message: toolbarMessages.strike,
tooltipKeymap: toggleStrikethrough
},
code: {
command: withInputMethod(toolbarType, toggleCodeWithAnalytics(editorAnalyticsAPI)),
message: toolbarMessages.code,
tooltipKeymap: toggleCode
},
subscript: {
command: withInputMethod(toolbarType, toggleSubscriptWithAnalytics(editorAnalyticsAPI)),
message: toolbarMessages.subscript,
tooltipKeymap: toggleSubscript
},
superscript: {
command: withInputMethod(toolbarType, toggleSuperscriptWithAnalytics(editorAnalyticsAPI)),
message: toolbarMessages.superscript,
tooltipKeymap: toggleSuperscript
}
});
const IconBefore = {
strong: {
icon: jsx(BoldIcon, {
color: "currentColor",
spacing: "spacious",
label: ""
})
},
em: {
icon: jsx(ItalicIcon, {
color: "currentColor",
spacing: "spacious",
label: ""
})
},
underline: {
icon: jsx(UnderlineIcon, {
color: "currentColor",
spacing: "spacious",
label: ""
})
},
strike: {
icon: jsx(TextStrikethroughIcon, {
label: ""
})
},
code: {
icon: jsx(AngleBracketsIcon, {
color: "currentColor",
spacing: "spacious",
label: ""
})
},
subscript: {
icon: jsx(Subscript, null)
},
superscript: {
icon: jsx(Superscript, null)
}
};
const getIcon = ({
iconType,
isDisabled,
isActive,
intl,
editorAnalyticsAPI,
toolbarType
}) => {
const icon = IconButtons(editorAnalyticsAPI, toolbarType)[iconType];
const iconBefore = IconBefore[iconType].icon;
const content = intl.formatMessage(icon.message);
const {
tooltipKeymap
} = icon;
return {
content,
buttonId: icon.buttonId,
iconMark: iconType,
key: iconType,
command: icon.command,
iconElement: icon.component ? icon.component() : undefined,
tooltipElement: tooltipKeymap ? jsx(ToolTipContent, {
description: content,
keymap: tooltipKeymap
}) : undefined,
elemBefore: editorExperiment('platform_editor_controls', 'variant1') ? iconBefore : undefined,
elemAfter: tooltipKeymap ?
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
jsx("div", {
css: shortcutStyle
}, tooltip(tooltipKeymap)) : undefined,
value: {
name: iconType
},
isActive,
isDisabled,
'aria-label': tooltipKeymap ? tooltip(tooltipKeymap, String(content)) : String(content),
'aria-keyshortcuts': getAriaKeyshortcuts(tooltipKeymap)
};
};
const IconsMarkSchema = {
[IconTypes.strong]: 'strong',
[IconTypes.em]: 'em',
[IconTypes.strike]: 'strike',
[IconTypes.code]: 'code',
[IconTypes.underline]: 'underline',
[IconTypes.superscript]: 'subsup',
[IconTypes.subscript]: 'subsup'
};
const buildMenuIconState = iconMark => ({
schema,
textFormattingState
}) => {
const hasPluginState = Boolean(textFormattingState === null || textFormattingState === void 0 ? void 0 : textFormattingState.isInitialised);
const markSchema = IconsMarkSchema[iconMark];
const hasSchemaMark = Boolean(schema.marks[markSchema]);
if (!hasPluginState) {
return {
isActive: false,
isDisabled: true,
isHidden: false,
hasSchemaMark
};
}
const isActive = textFormattingState === null || textFormattingState === void 0 ? void 0 : textFormattingState[`${iconMark}Active`];
const isDisabled = textFormattingState === null || textFormattingState === void 0 ? void 0 : textFormattingState[`${iconMark}Disabled`];
const isHidden = textFormattingState === null || textFormattingState === void 0 ? void 0 : textFormattingState[`${iconMark}Hidden`];
return {
isActive: Boolean(isActive),
isDisabled: Boolean(isDisabled),
isHidden: Boolean(isHidden),
hasSchemaMark
};
};
const buildIcon = (iconMark, editorAnalyticsAPI, toolbarType) => {
const getState = buildMenuIconState(iconMark);
return ({
schema,
textFormattingState,
intl,
isToolbarDisabled
}) => {
const iconState = getState({
schema,
textFormattingState
});
const {
isActive,
isDisabled,
isHidden,
hasSchemaMark
} = iconState;
const iconComponent = useMemo(() => getIcon({
iconType: IconTypes[iconMark],
isDisabled: isToolbarDisabled || isDisabled,
isActive,
intl,
editorAnalyticsAPI,
toolbarType
}), [isToolbarDisabled, isDisabled, isActive, intl]);
const shouldRenderIcon = hasSchemaMark && !isHidden;
return useMemo(() => shouldRenderIcon ? iconComponent : null, [shouldRenderIcon, iconComponent]);
};
};
export const useFormattingIcons = ({
isToolbarDisabled,
textFormattingState,
schema,
intl,
editorAnalyticsAPI,
toolbarType
}) => {
const props = {
schema,
textFormattingState,
intl,
isToolbarDisabled: Boolean(isToolbarDisabled),
toolbarType
};
const buildStrongIcon = buildIcon(IconTypes.strong, editorAnalyticsAPI, toolbarType);
const buildEmIcon = buildIcon(IconTypes.em, editorAnalyticsAPI, toolbarType);
const buildUnderlineIcon = buildIcon(IconTypes.underline, editorAnalyticsAPI, toolbarType);
const buildStrikeIcon = buildIcon(IconTypes.strike, editorAnalyticsAPI, toolbarType);
const buildCodeIcon = buildIcon(IconTypes.code, editorAnalyticsAPI, toolbarType);
const buildSubscriptIcon = buildIcon(IconTypes.subscript, editorAnalyticsAPI, toolbarType);
const buildSuperscriptIcon = buildIcon(IconTypes.superscript, editorAnalyticsAPI, toolbarType);
const strongIcon = buildStrongIcon(props);
const emIcon = buildEmIcon(props);
const underlineIcon = buildUnderlineIcon(props);
const strikeIcon = buildStrikeIcon(props);
const codeIcon = buildCodeIcon(props);
const subscriptIcon = buildSubscriptIcon(props);
const superscriptIcon = buildSuperscriptIcon(props);
const result = useMemo(() => [strongIcon, emIcon, underlineIcon, strikeIcon, codeIcon, subscriptIcon, superscriptIcon], [strongIcon, emIcon, underlineIcon, strikeIcon, codeIcon, subscriptIcon, superscriptIcon]);
return result;
};
export const useHasFormattingActived = ({
iconTypeList,
textFormattingState
}) => {
const hasActiveFormatting = useMemo(() => {
if (!(textFormattingState !== null && textFormattingState !== void 0 && textFormattingState.isInitialised)) {
return false;
}
return iconTypeList.some(iconType => textFormattingState[`${iconType}Active`]);
}, [textFormattingState, iconTypeList]);
return hasActiveFormatting;
};