mirror of
https://github.com/codex-team/editor.js
synced 2024-06-10 09:52:36 +02:00
Fix flipper tab after enter behaviour (#889)
* Fix flipper tab after enter behaviour * Fix: case when fillper opens to wrong block and disallow navigation by up/down * Update src/components/flipper.ts Co-Authored-By: Murod Khaydarov <murod.haydarov@gmail.com>
This commit is contained in:
parent
3167ebd323
commit
8a8fcccd0f
10
dist/editor.js
vendored
10
dist/editor.js
vendored
File diff suppressed because one or more lines are too long
|
@ -39,7 +39,7 @@ export default class DomIterator {
|
|||
nodeList: HTMLElement[],
|
||||
focusedCssClass: string,
|
||||
) {
|
||||
this.items = nodeList;
|
||||
this.items = nodeList || [];
|
||||
this.focusedCssClass = focusedCssClass;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,47 @@
|
|||
import DomIterator from './domIterator';
|
||||
import _ from './utils';
|
||||
|
||||
/**
|
||||
* Flipper construction options
|
||||
*/
|
||||
export interface FlipperOptions {
|
||||
/**
|
||||
* CSS-modifier for focused item
|
||||
*/
|
||||
focusedItemClass?: string;
|
||||
|
||||
/**
|
||||
* If flipping items are the same for all Block (for ex. Toolbox), ypu can pass it on constructing
|
||||
*/
|
||||
items?: HTMLElement[];
|
||||
|
||||
/**
|
||||
* Defines arrows usage. By default Flipper leafs items also via RIGHT/LEFT.
|
||||
*
|
||||
* true by default
|
||||
*
|
||||
* Pass 'false' if you don't need this behaviour
|
||||
* (for example, Inline Toolbar should be closed by arrows,
|
||||
* because it means caret moving with selection clearing)
|
||||
*/
|
||||
allowArrows?: boolean;
|
||||
|
||||
/**
|
||||
* Optional callback for button click
|
||||
*/
|
||||
activateCallback?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flipper is a component that iterates passed items array by TAB or Arrows and clicks it by ENTER
|
||||
*/
|
||||
export default class Flipper {
|
||||
|
||||
/**
|
||||
* Instance of flipper iterator
|
||||
* @type {DomIterator|null}
|
||||
*/
|
||||
private iterator: DomIterator = null;
|
||||
private readonly iterator: DomIterator = null;
|
||||
|
||||
/**
|
||||
* Flag that defines activation status
|
||||
|
@ -21,25 +53,23 @@ export default class Flipper {
|
|||
* Flag that allows arrows usage to flip items
|
||||
* @type {boolean}
|
||||
*/
|
||||
private allowArrows: boolean = true;
|
||||
private readonly allowArrows: boolean = true;
|
||||
|
||||
/**
|
||||
* Call back for button click/enter
|
||||
*/
|
||||
private readonly activateCallback: () => void;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
* @param {HTMLElement[]} nodeList - The list of iterable HTML-items
|
||||
* @param {string} focusedCssClass - CSS class name that will be set when item is focused
|
||||
* @param {boolean} allowArrows - Defines arrows usage. By default Flipper leafs items also via RIGHT/LEFT.
|
||||
* Pass 'false' if you don't need this behaviour
|
||||
* (for example, Inline Toolbar should be closed by arrows,
|
||||
* because it means caret moving with selection clearing)
|
||||
* @param {FlipperOptions} options - different constructing settings
|
||||
* @
|
||||
*/
|
||||
constructor(
|
||||
nodeList: HTMLElement[],
|
||||
focusedCssClass: string,
|
||||
allowArrows: boolean = true,
|
||||
) {
|
||||
this.allowArrows = allowArrows;
|
||||
this.iterator = new DomIterator(nodeList, focusedCssClass);
|
||||
constructor(options: FlipperOptions) {
|
||||
this.allowArrows = typeof options.allowArrows === 'boolean' ? options.allowArrows : true;
|
||||
this.iterator = new DomIterator(options.items, options.focusedItemClass);
|
||||
this.activateCallback = options.activateCallback;
|
||||
|
||||
/**
|
||||
* Listening all keydowns on document and react on TAB/Enter press
|
||||
|
@ -53,7 +83,13 @@ export default class Flipper {
|
|||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
/**
|
||||
* Prevent only used keys default behaviour
|
||||
* (allows to navigate by ARROW DOWN, for example)
|
||||
*/
|
||||
if (Flipper.usedKeys.includes(event.keyCode)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
switch (event.keyCode) {
|
||||
case _.keyCodes.TAB:
|
||||
|
@ -72,6 +108,21 @@ export default class Flipper {
|
|||
}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of keys (codes) that is handled by Flipper
|
||||
* Used to:
|
||||
* - preventDefault only for this keys, not all keywdowns (@see constructor)
|
||||
* - to skip external behaviours only for these keys, when filler is activated (@see BlockEvents@arrowRightAndDown)
|
||||
*/
|
||||
public static get usedKeys(): number[] {
|
||||
return [
|
||||
_.keyCodes.TAB,
|
||||
_.keyCodes.LEFT,
|
||||
_.keyCodes.RIGHT,
|
||||
_.keyCodes.ENTER,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Active tab/arrows handling by flipper
|
||||
* @param {HTMLElement[]} items - Some modules (like, InlineToolbar, BlockSettings) might refresh buttons dynamically
|
||||
|
@ -188,6 +239,10 @@ export default class Flipper {
|
|||
this.iterator.currentItem.click();
|
||||
}
|
||||
|
||||
if (typeof this.activateCallback === 'function') {
|
||||
this.activateCallback();
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import Module from '../__module';
|
||||
import _ from '../utils';
|
||||
import SelectionUtils from '../selection';
|
||||
import Flipper from "../flipper";
|
||||
|
||||
export default class BlockEvents extends Module {
|
||||
|
||||
|
@ -482,11 +483,18 @@ export default class BlockEvents extends Module {
|
|||
private arrowRightAndDown(event: KeyboardEvent): void {
|
||||
/**
|
||||
* Arrows might be handled on toolbars by flipper
|
||||
* Check for Flipper.usedKeys to allow navigate by DOWN and disallow by RIGHT
|
||||
*/
|
||||
if (this.Editor.UI.someToolbarOpened) {
|
||||
if (this.Editor.UI.someToolbarOpened && Flipper.usedKeys.includes(event.keyCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close Toolbar and highlighting when user moves cursor
|
||||
*/
|
||||
this.Editor.BlockManager.clearFocused();
|
||||
this.Editor.Toolbar.close();
|
||||
|
||||
const shouldEnableCBS = this.Editor.Caret.isAtEnd || this.Editor.BlockSelection.anyBlockSelected;
|
||||
|
||||
if (event.shiftKey && event.keyCode === _.keyCodes.DOWN && shouldEnableCBS) {
|
||||
|
@ -523,11 +531,18 @@ export default class BlockEvents extends Module {
|
|||
private arrowLeftAndUp(event: KeyboardEvent): void {
|
||||
/**
|
||||
* Arrows might be handled on toolbars by flipper
|
||||
* Check for Flipper.usedKeys to allow navigate by UP and disallow by LEFT
|
||||
*/
|
||||
if (this.Editor.UI.someToolbarOpened) {
|
||||
if (this.Editor.UI.someToolbarOpened && Flipper.usedKeys.includes(event.keyCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close Toolbar and highlighting when user moves cursor
|
||||
*/
|
||||
this.Editor.BlockManager.clearFocused();
|
||||
this.Editor.Toolbar.close();
|
||||
|
||||
const shouldEnableCBS = this.Editor.Caret.isAtStart || this.Editor.BlockSelection.anyBlockSelected;
|
||||
|
||||
if (event.shiftKey && event.keyCode === _.keyCodes.UP && shouldEnableCBS) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Module from '../../__module';
|
||||
import $ from '../../dom';
|
||||
import Flipper from '../../flipper';
|
||||
import Flipper, {FlipperOptions} from '../../flipper';
|
||||
import _ from '../../utils';
|
||||
|
||||
/**
|
||||
* Block Settings
|
||||
|
@ -185,6 +186,17 @@ export default class BlockSettings extends Module {
|
|||
* Buttons will be filled on opening
|
||||
*/
|
||||
private enableFlipper(): void {
|
||||
this.flipper = new Flipper([], this.CSS.focusedButton);
|
||||
this.flipper = new Flipper({
|
||||
focusedItemClass: this.CSS.focusedButton,
|
||||
activateCallback: () => {
|
||||
/**
|
||||
* Restoring focus on current Block after settings clicked.
|
||||
* For example, when H3 changed to H2 — DOM Elements replaced, so we need to focus a new one
|
||||
*/
|
||||
_.delay( () => {
|
||||
this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock);
|
||||
}, 10)();
|
||||
},
|
||||
} as FlipperOptions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,16 +82,16 @@ export default class ConversionToolbar extends Module {
|
|||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark current block's button with color
|
||||
*/
|
||||
this.highlightActiveTool(block.name);
|
||||
|
||||
this.move(block);
|
||||
|
||||
if (!this.opened) {
|
||||
this.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark current block's button with color
|
||||
*/
|
||||
this.highlightActiveTool(block.name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -99,7 +99,7 @@ export default class ConversionToolbar extends Module {
|
|||
*/
|
||||
public open(): void {
|
||||
this.opened = true;
|
||||
this.flipper.activate();
|
||||
this.flipper.activate(Object.values(this.tools));
|
||||
this.flipper.focusFirst();
|
||||
this.nodes.wrapper.classList.add(ConversionToolbar.CSS.conversionToolbarShowed);
|
||||
}
|
||||
|
@ -289,6 +289,8 @@ export default class ConversionToolbar extends Module {
|
|||
* Prepare Flipper to be able to leaf tools by arrows/tab
|
||||
*/
|
||||
private enableFlipper(): void {
|
||||
this.flipper = new Flipper(Object.values(this.tools), ConversionToolbar.CSS.conversionToolFocused);
|
||||
this.flipper = new Flipper({
|
||||
focusedItemClass: ConversionToolbar.CSS.conversionToolFocused,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -530,6 +530,9 @@ export default class InlineToolbar extends Module {
|
|||
* Buttons will be filled on opening
|
||||
*/
|
||||
private enableFlipper(): void {
|
||||
this.flipper = new Flipper([], this.CSS.focusedButton, false);
|
||||
this.flipper = new Flipper({
|
||||
focusedItemClass: this.CSS.focusedButton,
|
||||
allowArrows: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -320,7 +320,10 @@ export default class Toolbox extends Module {
|
|||
*/
|
||||
private enableFlipper(): void {
|
||||
const tools = Array.from(this.nodes.toolbox.childNodes) as HTMLElement[];
|
||||
this.flipper = new Flipper(tools, this.CSS.toolboxButtonActive);
|
||||
this.flipper = new Flipper({
|
||||
items: tools,
|
||||
focusedItemClass: this.CSS.toolboxButtonActive,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue