mirror of
https://github.com/codex-team/editor.js
synced 2024-06-10 18:03:25 +02:00
07b1ce2aca
* Add new popover class * Add flipper * confirmation * confirmation * Add confirmation support * Add search * Add toggle group support and update popover tests * Add custom content support * Fix scroll issue * Add mobile version * Integration * Fix animation * Cleanup * Fix popover position for narrow mode * Fix tests * Update version and changelog * Rename css classes * Move files * Stop using PopoverItem from outside of popover context * Fix jsdoc * Move error animation to popover item * Update css variables * Update docs/CHANGELOG.md Co-authored-by: Ilya Maroz <37909603+ilyamore88@users.noreply.github.com> * Update src/components/block-tunes/block-tune-move-down.ts Co-authored-by: Peter Savchenko <specc.dev@gmail.com> * Update src/components/block-tunes/block-tune-move-up.ts Co-authored-by: Peter Savchenko <specc.dev@gmail.com> * Fixes * Fix imports * Fix toolbox close event * Move search-input file * Fix comment * Rename method * Cleanup * Remove onFlip callback from popover item * Rename * Fix removing event listener * Move popover animations to popover.css file * Cleanup styles * Fix jsdoc * Fix confirmation chains * Close toolbox oly when it's open * Change activation error animation * Update version and changelog * Fix overlay * Update icon border-radius on mobile * Disable item text select * Update changelog * Update yarn.lock * Add rc postfix to version --------- Co-authored-by: Ilya Maroz <37909603+ilyamore88@users.noreply.github.com> Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
317 lines
8 KiB
TypeScript
317 lines
8 KiB
TypeScript
import Dom from '../../dom';
|
|
import { IconDotCircle } from '@codexteam/icons';
|
|
import { PopoverItem as PopoverItemParams } from '../../../../types';
|
|
|
|
/**
|
|
* Represents sigle popover item node
|
|
*/
|
|
export class PopoverItem {
|
|
/**
|
|
* True if item is disabled and hence not clickable
|
|
*/
|
|
public get isDisabled(): boolean {
|
|
return this.params.isDisabled;
|
|
}
|
|
|
|
/**
|
|
* Exposes popover item toggle parameter
|
|
*/
|
|
public get toggle(): boolean | string | undefined {
|
|
return this.params.toggle;
|
|
}
|
|
|
|
/**
|
|
* Item title
|
|
*/
|
|
public get title(): string | undefined {
|
|
return this.params.title;
|
|
}
|
|
|
|
/**
|
|
* True if popover should close once item is activated
|
|
*/
|
|
public get closeOnActivate(): boolean | undefined {
|
|
return this.params.closeOnActivate;
|
|
}
|
|
|
|
/**
|
|
* True if confirmation state is enabled for popover item
|
|
*/
|
|
public get isConfirmationStateEnabled(): boolean {
|
|
return this.confirmationState !== null;
|
|
}
|
|
|
|
/**
|
|
* True if item is focused in keyboard navigation process
|
|
*/
|
|
public get isFocused(): boolean {
|
|
return this.nodes.root.classList.contains(PopoverItem.CSS.focused);
|
|
}
|
|
|
|
/**
|
|
* Item html elements
|
|
*/
|
|
private nodes: {
|
|
root: null | HTMLElement,
|
|
icon: null | HTMLElement
|
|
} = {
|
|
root: null,
|
|
icon: null,
|
|
};
|
|
|
|
/**
|
|
* Popover item params
|
|
*/
|
|
private params: PopoverItemParams;
|
|
|
|
/**
|
|
* If item is in confirmation state, stores confirmation params such as icon, label, onActivate callback and so on
|
|
*/
|
|
private confirmationState: PopoverItemParams | null = null;
|
|
|
|
/**
|
|
* Popover item CSS classes
|
|
*/
|
|
public static get CSS(): {
|
|
container: string,
|
|
title: string,
|
|
secondaryTitle: string,
|
|
icon: string,
|
|
active: string,
|
|
disabled: string,
|
|
focused: string,
|
|
hidden: string,
|
|
confirmationState: string,
|
|
noHover: string,
|
|
noFocus: string,
|
|
wobbleAnimation: string
|
|
} {
|
|
return {
|
|
container: 'ce-popover-item',
|
|
title: 'ce-popover-item__title',
|
|
secondaryTitle: 'ce-popover-item__secondary-title',
|
|
icon: 'ce-popover-item__icon',
|
|
active: 'ce-popover-item--active',
|
|
disabled: 'ce-popover-item--disabled',
|
|
focused: 'ce-popover-item--focused',
|
|
hidden: 'ce-popover-item--hidden',
|
|
confirmationState: 'ce-popover-item--confirmation',
|
|
noHover: 'ce-popover-item--no-hover',
|
|
noFocus: 'ce-popover-item--no-focus',
|
|
wobbleAnimation: 'wobble',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Constructs popover item instance
|
|
*
|
|
* @param params - popover item construction params
|
|
*/
|
|
constructor(params: PopoverItemParams) {
|
|
this.params = params;
|
|
this.nodes.root = this.make(params);
|
|
}
|
|
|
|
/**
|
|
* Returns popover item root element
|
|
*/
|
|
public getElement(): HTMLElement {
|
|
return this.nodes.root;
|
|
}
|
|
|
|
/**
|
|
* Called on popover item click
|
|
*/
|
|
public handleClick(): void {
|
|
if (this.isConfirmationStateEnabled) {
|
|
this.activateOrEnableConfirmationMode(this.confirmationState);
|
|
|
|
return;
|
|
}
|
|
|
|
this.activateOrEnableConfirmationMode(this.params);
|
|
}
|
|
|
|
/**
|
|
* Toggles item active state
|
|
*
|
|
* @param isActive - true if item should strictly should become active
|
|
*/
|
|
public toggleActive(isActive?: boolean): void {
|
|
this.nodes.root.classList.toggle(PopoverItem.CSS.active, isActive);
|
|
}
|
|
|
|
/**
|
|
* Toggles item hidden state
|
|
*
|
|
* @param isHidden - true if item should be hidden
|
|
*/
|
|
public toggleHidden(isHidden: boolean): void {
|
|
this.nodes.root.classList.toggle(PopoverItem.CSS.hidden, isHidden);
|
|
}
|
|
|
|
/**
|
|
* Resets popover item to its original state
|
|
*/
|
|
public reset(): void {
|
|
if (this.isConfirmationStateEnabled) {
|
|
this.disableConfirmationMode();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Method called once item becomes focused during keyboard navigation
|
|
*/
|
|
public onFocus(): void {
|
|
this.disableSpecialHoverAndFocusBehavior();
|
|
}
|
|
|
|
/**
|
|
* Constructs HTML element corresponding to popover item params
|
|
*
|
|
* @param params - item construction params
|
|
*/
|
|
private make(params: PopoverItemParams): HTMLElement {
|
|
const el = Dom.make('div', PopoverItem.CSS.container);
|
|
|
|
if (params.name) {
|
|
el.dataset.itemName = params.name;
|
|
}
|
|
|
|
this.nodes.icon = Dom.make('div', PopoverItem.CSS.icon, {
|
|
innerHTML: params.icon || IconDotCircle,
|
|
});
|
|
|
|
el.appendChild(this.nodes.icon);
|
|
|
|
el.appendChild(Dom.make('div', PopoverItem.CSS.title, {
|
|
innerHTML: params.title || '',
|
|
}));
|
|
|
|
if (params.secondaryLabel) {
|
|
el.appendChild(Dom.make('div', PopoverItem.CSS.secondaryTitle, {
|
|
textContent: params.secondaryLabel,
|
|
}));
|
|
}
|
|
|
|
if (params.isActive) {
|
|
el.classList.add(PopoverItem.CSS.active);
|
|
}
|
|
|
|
if (params.isDisabled) {
|
|
el.classList.add(PopoverItem.CSS.disabled);
|
|
}
|
|
|
|
return el;
|
|
}
|
|
|
|
/**
|
|
* Activates confirmation mode for the item.
|
|
*
|
|
* @param newState - new popover item params that should be applied
|
|
*/
|
|
private enableConfirmationMode(newState: PopoverItemParams): void {
|
|
const params = {
|
|
...this.params,
|
|
...newState,
|
|
confirmation: newState.confirmation,
|
|
} as PopoverItemParams;
|
|
const confirmationEl = this.make(params);
|
|
|
|
this.nodes.root.innerHTML = confirmationEl.innerHTML;
|
|
this.nodes.root.classList.add(PopoverItem.CSS.confirmationState);
|
|
|
|
this.confirmationState = newState;
|
|
|
|
this.enableSpecialHoverAndFocusBehavior();
|
|
}
|
|
|
|
/**
|
|
* Returns item to its original state
|
|
*/
|
|
private disableConfirmationMode(): void {
|
|
const itemWithOriginalParams = this.make(this.params);
|
|
|
|
this.nodes.root.innerHTML = itemWithOriginalParams.innerHTML;
|
|
this.nodes.root.classList.remove(PopoverItem.CSS.confirmationState);
|
|
|
|
this.confirmationState = null;
|
|
|
|
this.disableSpecialHoverAndFocusBehavior();
|
|
}
|
|
|
|
/**
|
|
* Enables special focus and hover behavior for item in confirmation state.
|
|
* This is needed to prevent item from being highlighted as hovered/focused just after click.
|
|
*/
|
|
private enableSpecialHoverAndFocusBehavior(): void {
|
|
this.nodes.root.classList.add(PopoverItem.CSS.noHover);
|
|
this.nodes.root.classList.add(PopoverItem.CSS.noFocus);
|
|
|
|
this.nodes.root.addEventListener('mouseleave', this.removeSpecialHoverBehavior, { once: true });
|
|
}
|
|
|
|
/**
|
|
* Disables special focus and hover behavior
|
|
*/
|
|
private disableSpecialHoverAndFocusBehavior(): void {
|
|
this.removeSpecialFocusBehavior();
|
|
this.removeSpecialHoverBehavior();
|
|
|
|
this.nodes.root.removeEventListener('mouseleave', this.removeSpecialHoverBehavior);
|
|
}
|
|
|
|
/**
|
|
* Removes class responsible for special focus behavior on an item
|
|
*/
|
|
private removeSpecialFocusBehavior = (): void => {
|
|
this.nodes.root.classList.remove(PopoverItem.CSS.noFocus);
|
|
};
|
|
|
|
/**
|
|
* Removes class responsible for special hover behavior on an item
|
|
*/
|
|
private removeSpecialHoverBehavior = (): void => {
|
|
this.nodes.root.classList.remove(PopoverItem.CSS.noHover);
|
|
};
|
|
|
|
/**
|
|
* Executes item's onActivate callback if the item has no confirmation configured
|
|
*
|
|
* @param item - item to activate or bring to confirmation mode
|
|
*/
|
|
private activateOrEnableConfirmationMode(item: PopoverItemParams): void {
|
|
if (item.confirmation === undefined) {
|
|
try {
|
|
item.onActivate(item);
|
|
this.disableConfirmationMode();
|
|
} catch {
|
|
this.animateError();
|
|
}
|
|
} else {
|
|
this.enableConfirmationMode(item.confirmation);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Animates item which symbolizes that error occured while executing 'onActivate()' callback
|
|
*/
|
|
private animateError(): void {
|
|
if (this.nodes.icon.classList.contains(PopoverItem.CSS.wobbleAnimation)) {
|
|
return;
|
|
}
|
|
|
|
this.nodes.icon.classList.add(PopoverItem.CSS.wobbleAnimation);
|
|
|
|
this.nodes.icon.addEventListener('animationend', this.onErrorAnimationEnd);
|
|
}
|
|
|
|
/**
|
|
* Handles finish of error animation
|
|
*/
|
|
private onErrorAnimationEnd = (): void => {
|
|
this.nodes.icon.classList.remove(PopoverItem.CSS.wobbleAnimation);
|
|
this.nodes.icon.removeEventListener('animationend', this.onErrorAnimationEnd);
|
|
};
|
|
}
|