mirror of
https://github.com/codex-team/editor.js
synced 2024-06-04 15:02:42 +02:00
8f156a87ea
* the popover component, vertical toolbox * toolbox position improved * popover width improved * always show the plus button * search field added * search input in popover * trying to create mobile toolbox * feat(toolbox): popover adapted for mobile devices (#2004) * FIx mobile popover fixed positioning * Add mobile popover overlay * Hide mobile popover on scroll * Alter toolbox buttons hover * Fix closing popover on overlay click * Tests fix * Fix onchange test * restore focus after toolbox closing by ESC * don't move toolbar by block-hover on mobile Resolves #1972 * popover mobile styles improved * Cleanup * Remove scroll event listener * Lock scroll on mobile * don't show shortcuts in mobile popover * Change data attr name * Remove unused styles * Remove unused listeners * disable hover on mobile popover * Scroll fix * Lint * Revert "Scroll fix" This reverts commit82deae543e
. * Return back background color for active state of toolbox buttons Co-authored-by: Peter Savchenko <specc.dev@gmail.com> * Vertical toolbox fixes (#2017) * Replace visibility property with display for hiding popover * Disable arrow right and left keys for popover * Revert "Replace visibility property with display for hiding popover" This reverts commitaf521cf6f2
. * Hide popover via setting max-height to 0 to fix animation in safari * Remove redundant condition * Extend element interface to avoid ts errors * Do not subscribe to block hovered if mobile * Add unsubscribing from overlay click event * Rename isMobile to isMobileScreen * Cleanup * fix: popover opening direction (#2022) * Change popover opening direction based on available space below it * Update check * Use cacheable decorator * Update src/components/flipper.ts Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com> * Fixes * Fix test * Clear search on popover hide * Fix popover width * Fix for tests * Update todos * Linter fixes * rm todo about beforeInsert because I have no idea what does it mean * i18n for search labels done * rm methods for hiding/showing of + * some code style update * Update CHANGELOG.md * make the list items a little bit compact * fix z-index issue caused by block-appearing animation also, improve popover padding for two reasons: - make the popover more consistent with the Table tool popover (in future, it can be done with the same api method) - make popover looks better Co-authored-by: Tanya Fomina <fomina.tatianaaa@yandex.ru> Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
263 lines
5.9 KiB
TypeScript
263 lines
5.9 KiB
TypeScript
import DomIterator from './domIterator';
|
|
import * as _ from './utils';
|
|
|
|
/**
|
|
* Flipper construction options
|
|
*
|
|
* @interface FlipperOptions
|
|
*/
|
|
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[];
|
|
|
|
/**
|
|
* Optional callback for button click
|
|
*/
|
|
activateCallback?: (item: HTMLElement) => void;
|
|
|
|
/**
|
|
* List of keys allowed for handling.
|
|
* Can include codes of the following keys:
|
|
* - Tab
|
|
* - Enter
|
|
* - Arrow up
|
|
* - Arrow down
|
|
* - Arrow right
|
|
* - Arrow left
|
|
* If not specified all keys are enabled
|
|
*/
|
|
allowedKeys?: number[];
|
|
}
|
|
|
|
/**
|
|
* 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 readonly iterator: DomIterator = null;
|
|
|
|
/**
|
|
* Flag that defines activation status
|
|
*
|
|
* @type {boolean}
|
|
*/
|
|
private activated = false;
|
|
|
|
/**
|
|
* List codes of the keys allowed for handling
|
|
*/
|
|
private readonly allowedKeys: number[];
|
|
|
|
/**
|
|
* Call back for button click/enter
|
|
*/
|
|
private readonly activateCallback: (item: HTMLElement) => void;
|
|
|
|
/**
|
|
* @param {FlipperOptions} options - different constructing settings
|
|
*/
|
|
constructor(options: FlipperOptions) {
|
|
this.iterator = new DomIterator(options.items, options.focusedItemClass);
|
|
this.activateCallback = options.activateCallback;
|
|
this.allowedKeys = options.allowedKeys || Flipper.usedKeys;
|
|
}
|
|
|
|
/**
|
|
* Array of keys (codes) that is handled by Flipper
|
|
* Used to:
|
|
* - preventDefault only for this keys, not all keydowns (@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,
|
|
_.keyCodes.UP,
|
|
_.keyCodes.DOWN,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Active tab/arrows handling by flipper
|
|
*
|
|
* @param {HTMLElement[]} items - Some modules (like, InlineToolbar, BlockSettings) might refresh buttons dynamically
|
|
*/
|
|
public activate(items?: HTMLElement[]): void {
|
|
this.activated = true;
|
|
|
|
if (items) {
|
|
this.iterator.setItems(items);
|
|
}
|
|
|
|
/**
|
|
* Listening all keydowns on document and react on TAB/Enter press
|
|
* TAB will leaf iterator items
|
|
* ENTER will click the focused item
|
|
*/
|
|
document.addEventListener('keydown', this.onKeyDown);
|
|
}
|
|
|
|
/**
|
|
* Disable tab/arrows handling by flipper
|
|
*/
|
|
public deactivate(): void {
|
|
this.activated = false;
|
|
this.dropCursor();
|
|
|
|
document.removeEventListener('keydown', this.onKeyDown);
|
|
}
|
|
|
|
/**
|
|
* Focus first item
|
|
*/
|
|
public focusFirst(): void {
|
|
this.dropCursor();
|
|
this.flipRight();
|
|
}
|
|
|
|
/**
|
|
* Focuses previous flipper iterator item
|
|
*/
|
|
public flipLeft(): void {
|
|
this.iterator.previous();
|
|
this.flipCallback();
|
|
}
|
|
|
|
/**
|
|
* Focuses next flipper iterator item
|
|
*/
|
|
public flipRight(): void {
|
|
this.iterator.next();
|
|
this.flipCallback();
|
|
}
|
|
|
|
/**
|
|
* Return true if some button is focused
|
|
*/
|
|
public hasFocus(): boolean {
|
|
return !!this.iterator.currentItem;
|
|
}
|
|
|
|
/**
|
|
* Drops flipper's iterator cursor
|
|
*
|
|
* @see DomIterator#dropCursor
|
|
*/
|
|
private dropCursor(): void {
|
|
this.iterator.dropCursor();
|
|
}
|
|
|
|
/**
|
|
* KeyDown event handler
|
|
*
|
|
* @param event - keydown event
|
|
*/
|
|
private onKeyDown = (event): void => {
|
|
const isReady = this.isEventReadyForHandling(event);
|
|
|
|
if (!isReady) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* 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:
|
|
this.handleTabPress(event);
|
|
break;
|
|
case _.keyCodes.LEFT:
|
|
case _.keyCodes.UP:
|
|
this.flipLeft();
|
|
break;
|
|
case _.keyCodes.RIGHT:
|
|
case _.keyCodes.DOWN:
|
|
this.flipRight();
|
|
break;
|
|
case _.keyCodes.ENTER:
|
|
this.handleEnterPress(event);
|
|
break;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* This function is fired before handling flipper keycodes
|
|
* The result of this function defines if it is need to be handled or not
|
|
*
|
|
* @param {KeyboardEvent} event - keydown keyboard event
|
|
* @returns {boolean}
|
|
*/
|
|
private isEventReadyForHandling(event: KeyboardEvent): boolean {
|
|
return this.activated && this.allowedKeys.includes(event.keyCode);
|
|
}
|
|
|
|
/**
|
|
* When flipper is activated tab press will leaf the items
|
|
*
|
|
* @param {KeyboardEvent} event - tab keydown event
|
|
*/
|
|
private handleTabPress(event: KeyboardEvent): void {
|
|
/** this property defines leaf direction */
|
|
const shiftKey = event.shiftKey,
|
|
direction = shiftKey ? DomIterator.directions.LEFT : DomIterator.directions.RIGHT;
|
|
|
|
switch (direction) {
|
|
case DomIterator.directions.RIGHT:
|
|
this.flipRight();
|
|
break;
|
|
case DomIterator.directions.LEFT:
|
|
this.flipLeft();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enter press will click current item if flipper is activated
|
|
*
|
|
* @param {KeyboardEvent} event - enter keydown event
|
|
*/
|
|
private handleEnterPress(event: KeyboardEvent): void {
|
|
if (!this.activated) {
|
|
return;
|
|
}
|
|
|
|
if (this.iterator.currentItem) {
|
|
this.iterator.currentItem.click();
|
|
}
|
|
|
|
if (_.isFunction(this.activateCallback)) {
|
|
this.activateCallback(this.iterator.currentItem);
|
|
}
|
|
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
|
|
/**
|
|
* Fired after flipping in any direction
|
|
*/
|
|
private flipCallback(): void {
|
|
if (this.iterator.currentItem) {
|
|
this.iterator.currentItem.scrollIntoViewIfNeeded();
|
|
}
|
|
}
|
|
}
|