suneditor
Version:
Vanilla JavaScript based WYSIWYG web editor
136 lines (120 loc) • 4.48 kB
JavaScript
import { env, dom, keyCodeMap } from '../../../helper';
import { reduceBackspaceDown } from '../rules/keydown.rule.backspace';
import { reduceDeleteDown } from '../rules/keydown.rule.delete';
import { reduceEnterDown } from '../rules/keydown.rule.enter';
import { reduceTabDown } from '../rules/keydown.rule.tab';
import { reduceArrowDown } from '../rules/keydown.rule.arrow';
import { A } from '../actions';
const { isOSX_IOS } = env;
/**
* @description Master switch for processing Enter from the `beforeinput` event (post-IME-commit) instead
* of `keydown`, to avoid trapping iOS/mobile IME marked-text when `keydown` mutates the DOM (see
* `handler_ww_input.js`).
*
* This is only the compile-time master; the effective per-environment decision is
* {@link useEnterFromBeforeInput}, which additionally requires that this environment actually delivers
* `beforeinput` at runtime (some corporate security SW / DLP / VDI drop it).
* @type {boolean}
*/
export const ENTER_FROM_BEFOREINPUT = true;
/**
* @description Routing decision for Enter. Auto-repeat (held key) fires `keydown` but not `beforeinput`,
* so a repeat must stay on `keydown` or only the first line break would land.
* @param {SunEditor.Store} store - Editor store object
* @param {KeyboardEvent|InputEvent} [e] - Source event; a `repeat` flag forces the `keydown` path.
* @returns {boolean} `true` to route Enter through `beforeinput`, `false` to keep it on `keydown`.
*/
export function useEnterFromBeforeInput(store, e) {
return ENTER_FROM_BEFOREINPUT && store.get('_canUseBeforeInput') && !(/** @type {KeyboardEvent} */ (e)?.repeat);
}
/**
* @typedef {import('../ports').EventReducerPorts} EventPorts
*/
/**
* @typedef {Object} KeydownReducerCtx - Keydown Reducer Context object
* @property {KeyboardEvent|InputEvent} ctx.e - The keyboard event (or the `beforeinput` InputEvent when Enter is dispatched from `beforeinput`)
* @property {SunEditor.FrameContext} ctx.fc - Frame context object
* @property {SunEditor.Store} ctx.store - Editor store object
* @property {SunEditor.Options} ctx.options - Options object
* @property {SunEditor.FrameOptions} ctx.frameOptions - Frame options object
* @property {Range} ctx.range - Current selection range
* @property {HTMLElement|Text} ctx.selectionNode - Current selection node
* @property {HTMLElement} ctx.formatEl - Current format element
* @property {string} ctx.keyCode - Key code
* @property {boolean} ctx.ctrl - Whether the `ctrl` key is pressed
* @property {boolean} ctx.alt - Whether the `alt` key is pressed
* @property {boolean} ctx.shift - Whether the `shift` key is pressed
*/
/**
* @typedef {import('../actions').Action[]} EventActions
*/
/**
* @description Keydown event reducer
* @param {EventPorts} ports - Ports for interacting with editor
* @param {KeydownReducerCtx} ctx - Context object
* @returns {Promise<EventActions>} Action list
*/
export async function reduceKeydown(ports, ctx) {
const actions = [];
switch (ctx.keyCode) {
case 'Backspace' /** backspace key */: {
if (reduceBackspaceDown(actions, ports, ctx) === false) {
return actions;
}
break;
}
case 'Delete' /** delete key */: {
if (reduceDeleteDown(actions, ports, ctx) === false) {
return actions;
}
break;
}
case 'Tab' /** tab key */: {
if (reduceTabDown(actions, ports, ctx) === false) {
return actions;
}
break;
}
case 'Enter' /** enter key */: {
if (!useEnterFromBeforeInput(ctx.store, ctx.e)) {
if (reduceEnterDown(actions, ports, ctx) === false) {
return actions;
}
}
break;
}
}
// ZWS, nbsp, documentType
const { fc, keyCode, shift, alt, ctrl, range } = ctx;
if (shift && (isOSX_IOS ? alt : ctrl) && keyCodeMap.isSpace(keyCode)) {
actions.push(A.preventStop());
actions.push(A.keydownInputInsertNbsp());
return actions;
}
const selectRange = !range.collapsed || range.startContainer !== range.endContainer;
if (
!ctrl &&
!alt &&
!selectRange &&
!keyCodeMap.isNonTextKey(keyCode) &&
dom.check.isBreak(range.commonAncestorContainer)
) {
actions.push(A.keydownInputInsertZWS());
return actions;
}
// document type
if (
fc.has('documentType_use_header') &&
selectRange &&
!ctrl &&
!alt &&
!shift &&
!keyCodeMap.isDirectionKey(keyCode)
) {
actions.push(A.documentTypeRefreshHeader());
return actions;
}
// Arrow key - select component action
reduceArrowDown(actions, ports, ctx);
return actions;
}