Skip to content

Commit

Permalink
Fix flipper tab after enter behaviour (#889)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
neSpecc and khaydarov authored Sep 8, 2019
1 parent 3167ebd commit 8a8fccc
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 36 deletions.
10 changes: 5 additions & 5 deletions dist/editor.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/components/domIterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default class DomIterator {
nodeList: HTMLElement[],
focusedCssClass: string,
) {
this.items = nodeList;
this.items = nodeList || [];
this.focusedCssClass = focusedCssClass;
}

Expand Down
89 changes: 72 additions & 17 deletions src/components/flipper.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
*/
constructor(
nodeList: HTMLElement[],
focusedCssClass: string,
allowArrows: boolean = true,
) {
this.allowArrows = allowArrows;
this.iterator = new DomIterator(nodeList, focusedCssClass);
* @param {FlipperOptions} options - different constructing settings
* @
*/
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
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -188,6 +239,10 @@ export default class Flipper {
this.iterator.currentItem.click();
}

if (typeof this.activateCallback === 'function') {
this.activateCallback();
}

event.preventDefault();
event.stopPropagation();
}
Expand Down
19 changes: 17 additions & 2 deletions src/components/modules/blockEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
16 changes: 14 additions & 2 deletions src/components/modules/toolbar/blockSettings.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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);
}
}
16 changes: 9 additions & 7 deletions src/components/modules/toolbar/conversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,24 +82,24 @@ 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);
}

/**
* Shows Conversion Toolbar
*/
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);
}
Expand Down Expand Up @@ -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,
});
}
}
5 changes: 4 additions & 1 deletion src/components/modules/toolbar/inline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
}
}
5 changes: 4 additions & 1 deletion src/components/modules/toolbar/toolbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
}

/**
Expand Down

0 comments on commit 8a8fccc

Please sign in to comment.