UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

258 lines (256 loc) • 12.7 kB
/** @jsx jsx */ import React, { Fragment } from 'react'; import { css, jsx } from '@emotion/react'; import { base, keyName } from 'w3c-keyname'; import { N400 } from '@atlaskit/theme/colors'; import { editorCommandToPMCommand } from '../preset/editor-commands'; import { browser } from '../utils'; export const addAltText = makeKeyMapWithCommon('Add Alt Text', 'Mod-Alt-y'); export const navToEditorToolbar = makeKeyMapWithCommon('Navigate to editor toolbar', 'Alt-F9'); export const navToFloatingToolbar = makeKeyMapWithCommon('Navigate to floating toolbar', 'Alt-F10'); export const toggleBold = makeKeyMapWithCommon('Bold', 'Mod-b'); export const toggleItalic = makeKeyMapWithCommon('Italic', 'Mod-i'); export const toggleUnderline = makeKeyMapWithCommon('Underline', 'Mod-u'); export const toggleStrikethrough = makeKeyMapWithCommon('Strikethrough', 'Mod-Shift-s'); export const toggleSubscript = makeKeyMapWithCommon('Subscript', 'Mod-Shift-,'); export const toggleSuperscript = makeKeyMapWithCommon('Superscript', 'Mod-Shift-.'); export const toggleCode = makeKeyMapWithCommon('Code', 'Mod-Shift-m'); export const pastePlainText = makeKeyMapWithCommon('Paste Plain Text', 'Mod-Shift-v'); export const clearFormatting = makeKeyMapWithCommon('Clear formatting', 'Mod-\\'); export const setNormalText = makeKeyMapWithCommon('Normal text', 'Mod-Alt-0'); export const toggleHeading1 = makeKeyMapWithCommon('Heading 1', 'Mod-Alt-1'); export const toggleHeading2 = makeKeyMapWithCommon('Heading 2', 'Mod-Alt-2'); export const toggleHeading3 = makeKeyMapWithCommon('Heading 3', 'Mod-Alt-3'); export const toggleHeading4 = makeKeyMapWithCommon('Heading 4', 'Mod-Alt-4'); export const toggleHeading5 = makeKeyMapWithCommon('Heading 5', 'Mod-Alt-5'); export const toggleHeading6 = makeKeyMapWithCommon('Heading 6', 'Mod-Alt-6'); export const toggleOrderedList = makeKeyMapWithCommon('Numbered list', 'Mod-Shift-7'); export const ctrlBackSpace = makeKeyMapWithCommon('Cmd + Backspace', 'Mod-Backspace'); export const toggleBulletList = makeKeyMapWithCommon('Bullet list', 'Mod-Shift-8'); export const toggleBlockQuote = makeKeyMapWithCommon('Quote', 'Mod-Shift-9'); export const insertNewLine = makeKeyMapWithCommon('Insert new line', 'Shift-Enter'); export const shiftBackspace = makeKeyMapWithCommon('Shift Backspace', 'Shift-Backspace'); export const splitCodeBlock = makeKeyMapWithCommon('Split code block', 'Enter'); export const splitListItem = makeKeyMapWithCommon('Split list item', 'Enter'); export const insertRule = makeKeyMapWithCommon('Insert horizontal rule', 'Mod-Shift--'); export const undo = makeKeyMapWithCommon('Undo', 'Mod-z'); export const moveUp = makeKeyMapWithCommon('Move up', 'ArrowUp'); export const moveDown = makeKeyMapWithCommon('Move down', 'ArrowDown'); export const moveLeft = makeKeyMapWithCommon('Move left', 'ArrowLeft'); export const moveRight = makeKeyMapWithCommon('Move right', 'ArrowRight'); export const indentList = makeKeyMapWithCommon('Indent List', 'Tab'); export const outdentList = makeKeyMapWithCommon('Outdent List', 'Shift-Tab'); export const redo = makeKeymap('Redo', 'Ctrl-y', 'Mod-Shift-z'); export const openHelp = makeKeyMapWithCommon('Open Help', 'Mod-/'); export const addLink = makeKeyMapWithCommon('Link', 'Mod-k'); export const addInlineComment = makeKeyMapWithCommon('Annotate', 'Mod-Alt-c'); export const submit = makeKeyMapWithCommon('Submit Content', 'Mod-Enter'); export const enter = makeKeyMapWithCommon('Enter', 'Enter'); export const shiftEnter = makeKeyMapWithCommon('Shift Enter', 'Shift-Enter'); export const tab = makeKeyMapWithCommon('Tab', 'Tab'); export const indent = makeKeyMapWithCommon('Indent', 'Tab'); export const outdent = makeKeyMapWithCommon('Outdent', 'Shift-Tab'); export const backspace = makeKeyMapWithCommon('Backspace', 'Backspace'); export const deleteKey = makeKeyMapWithCommon('Delete', 'Delete'); export const forwardDelete = makeKeymap('Forward Delete', '', 'Ctrl-d'); export const space = makeKeyMapWithCommon('Space', 'Space'); export const escape = makeKeyMapWithCommon('Escape', 'Escape'); export const nextCell = makeKeyMapWithCommon('Next cell', 'Tab'); export const previousCell = makeKeyMapWithCommon('Previous cell', 'Shift-Tab'); export const shiftArrowUp = makeKeyMapWithCommon('Shift ArrowUp', 'Shift-ArrowUp'); export const shiftTab = makeKeyMapWithCommon('Shift Tab', 'Shift-Tab'); export const toggleTable = makeKeyMapWithCommon('Table', 'Shift-Alt-t'); export const focusTableResizer = makeKeyMapWithCommon('Focus Table Resizer', 'Mod-Alt-Shift-r'); export const addRowBefore = makeKeyMapWithCommon('Add Row Above', 'Ctrl-Alt-ArrowUp'); export const addRowAfter = makeKeyMapWithCommon('Add Row Below', 'Ctrl-Alt-ArrowDown'); export const addColumnAfter = makeKeyMapWithCommon('Add Column After', 'Ctrl-Alt-ArrowRight'); export const addColumnBefore = makeKeyMapWithCommon('Add Column Before', 'Ctrl-Alt-ArrowLeft'); export const moveColumnLeft = makeKeyMapWithCommon('Move Column Left', 'Ctrl-Alt-Shift-ArrowLeft'); export const moveColumnRight = makeKeyMapWithCommon('Move Column Right', 'Ctrl-Alt-Shift-ArrowRight'); export const moveRowDown = makeKeyMapWithCommon('Move Row Down', 'Ctrl-Alt-Shift-ArrowDown'); export const moveRowUp = makeKeyMapWithCommon('Move Row Up', 'Ctrl-Alt-Shift-ArrowUp'); export const deleteColumn = makeKeyMapWithCommon('Delete Column', 'Ctrl-Backspace'); export const deleteRow = makeKeyMapWithCommon('Delete Row', 'Ctrl-Backspace'); export const startColumnResizing = makeKeyMapWithCommon('Activate Column Resize', 'Mod-Alt-Shift-C'); export const cut = makeKeyMapWithCommon('Cut', 'Mod-x'); export const copy = makeKeyMapWithCommon('Copy', 'Mod-c'); export const paste = makeKeyMapWithCommon('Paste', 'Mod-v'); export const altPaste = makeKeyMapWithCommon('Paste', 'Mod-Shift-v'); export const find = makeKeyMapWithCommon('Find', 'Mod-f'); export const alignLeft = makeKeyMapWithCommon('Align Left', 'Mod-Shift-l'); export const alignCenter = makeKeyMapWithCommon('Align Center', 'Mod-Alt-e'); export const alignRight = makeKeyMapWithCommon('Align Right', 'Mod-Alt-t'); export const toggleTaskItemCheckbox = makeKeyMapWithCommon('Toggles task item', 'Mod-Alt-Enter'); export const selectRow = makeKeyMapArrayWithCommon('Select row', ['Mod-Alt-Shift-ArrowLeft', 'Mod-Alt-Shift-ArrowRight']); export const selectColumn = makeKeyMapArrayWithCommon('Select column', ['Mod-Alt-Shift-ArrowDown', 'Mod-Alt-Shift-ArrowUp']); export const selectTable = makeKeyMapWithCommon('Select table', 'Mod-a'); export const increaseMediaSize = makeKeyMapWithCommon('increase image size', 'Mod-Alt-]'); export const decreaseMediaSize = makeKeyMapWithCommon('increase image size', 'Mod-Alt-['); const arrowKeysMap = { // for reference: https://wincent.com/wiki/Unicode_representations_of_modifier_keys ARROWLEFT: '\u2190', ARROWRIGHT: '\u2192', ARROWUP: '\u2191', ARROWDOWN: '\u2193' }; const tooltipShortcutStyle = css({ borderRadius: '2px', backgroundColor: `var(--ds-background-inverse-subtle, ${N400})`, padding: `0 ${"var(--ds-space-025, 2px)"}`, // NOTE: This might not actually do anything: https://atlassian.slack.com/archives/CFG3PSQ9E/p1647395052443259?thread_ts=1647394572.556029&cid=CFG3PSQ9E label: 'tooltip-shortcut' }); function formatShortcut(keymap) { let shortcut; if (browser.mac) { // for reference: https://wincent.com/wiki/Unicode_representations_of_modifier_keys shortcut = keymap.mac.replace(/Cmd/i, '\u2318').replace(/Shift/i, '\u21E7').replace(/Ctrl/i, '\u2303').replace(/Alt/i, '\u2325').replace(/Backspace/i, '\u232B').replace(/Enter/i, '\u23CE'); } else { shortcut = keymap.windows.replace(/Backspace/i, '\u232B'); } const keys = shortcut.split('-'); let lastKey = keys[keys.length - 1]; if (lastKey.length === 1) { // capitalise single letters lastKey = lastKey.toUpperCase(); } keys[keys.length - 1] = arrowKeysMap[lastKey.toUpperCase()] || lastKey; return keys.join(browser.mac ? '' : '+'); } export function tooltip(keymap, description) { if (keymap) { const shortcut = formatShortcut(keymap); return description ? `${description} ${shortcut}` : shortcut; } return; } export const ToolTipContent = /*#__PURE__*/React.memo(({ description, shortcutOverride, keymap }) => { const shortcut = shortcutOverride || keymap && formatShortcut(keymap); return shortcut || description ? jsx(Fragment, null, description, shortcut && description && '\u00A0', shortcut && jsx("span", { css: tooltipShortcutStyle }, shortcut)) : null; }); export function findKeymapByDescription(description) { const matches = ALL.filter(keymap => keymap.description.toUpperCase() === description.toUpperCase()); return matches[0]; } export function findShortcutByDescription(description) { const keymap = findKeymapByDescription(description); if (keymap) { return findShortcutByKeymap(keymap); } return; } export function findShortcutByKeymap(keymap) { if (browser.mac) { return keymap.mac; } return keymap.windows; } export function getAriaKeyshortcuts(keymap) { let keyShortcuts; if (typeof keymap === 'string') { keyShortcuts = keymap; } else if (typeof keymap === 'object') { keyShortcuts = keymap[browser.mac ? 'mac' : 'windows']; } if (keyShortcuts) { return keyShortcuts.toLowerCase().split('-').map(modifier => { switch (modifier) { case 'cmd': return 'Meta'; case 'ctrl': return 'Control'; case 'alt': return 'Alt'; case 'shift': return 'Shift'; case 'enter': return 'Enter'; case 'esc': return 'Esc'; case 'tab': return 'Tab'; case 'space': return 'Space'; case 'backspace': return 'Backspace'; default: return modifier.split('').join(' '); } }).join('+'); } else { return undefined; } } const ALL = [toggleOrderedList, toggleBulletList, toggleBold, toggleItalic, toggleUnderline, toggleStrikethrough, toggleSubscript, toggleSuperscript, toggleCode, setNormalText, toggleHeading1, toggleHeading2, toggleHeading3, toggleHeading4, toggleHeading5, toggleHeading6, toggleBlockQuote, insertNewLine, insertRule, splitCodeBlock, splitListItem, redo, undo, find, escape, enter, shiftEnter]; export function makeKeymap(description, windows, mac, common) { return { description: description, windows: windows.replace(/Mod/i, 'Ctrl'), mac: mac.replace(/Mod/i, 'Cmd'), common: common }; } export function makeKeyMapWithCommon(description, common) { const windows = common.replace(/Mod/i, 'Ctrl'); const mac = common.replace(/Mod/i, 'Cmd'); return makeKeymap(description, windows, mac, common); } export function makeKeyMapArrayWithCommon(description, shortcuts) { const keymapArray = []; shortcuts.forEach(shortcut => { keymapArray.push(makeKeyMapWithCommon(description, shortcut)); }); return keymapArray; } function combineWithOldCommand(cmd, oldCmd) { return (state, dispatch, editorView) => { return oldCmd(state, dispatch) || cmd(state, dispatch, editorView); }; } export function bindKeymapWithCommand(shortcut, cmd, keymap) { const oldCmd = keymap[shortcut]; keymap[shortcut] = oldCmd ? combineWithOldCommand(cmd, oldCmd) : cmd; } // If there is a need to use the same command with several shortcuts export function bindKeymapArrayWithCommand(shortcutsArray, cmd, keymap) { shortcutsArray.forEach(shortcut => { return bindKeymapWithCommand(shortcut.common, cmd, keymap); }); } export function bindKeymapWithEditorCommand(shortcut, cmd, keymap) { bindKeymapWithCommand(shortcut, editorCommandToPMCommand(cmd), keymap); } export function findKeyMapForBrowser(keyMap) { if (keyMap) { if (browser.mac) { return keyMap.mac; } return keyMap.windows; } return; } /** * ED-20175: on windows OS if the capsLock is ON then it registers the key with capital case * which creates a command (Ctrl-B) and all the keymap bindings are in lower case (Ctrl-b). * */ export function isCapsLockOnAndModifyKeyboardEvent(event) { let keyboardEvent = event; const name = keyName(event); if (event.ctrlKey && event.getModifierState('CapsLock') && !event.getModifierState('Shift') && name.length === 1 && /^[A-Z]/.test(name)) { keyboardEvent = new KeyboardEvent('keydown', { key: base[event.keyCode].toLowerCase(), code: event.code, ctrlKey: true }); } return keyboardEvent; } export { DOWN, HEADING_KEYS, KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, LEFT, RIGHT, UP } from './consts'; export { keymap } from './keymap';