This commit is contained in:
Tanya Fomina 2024-05-11 17:42:00 +03:00
parent ecc8204658
commit 1a1120a421
13 changed files with 224 additions and 73 deletions

View file

@ -54,16 +54,31 @@ export default class BoldInlineTool implements InlineTool {
button: undefined,
};
// /**
// * Create button for Inline Toolbar
// */
// public render(): HTMLElement {
// this.nodes.button = document.createElement('button') as HTMLButtonElement;
// this.nodes.button.type = 'button';
// this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier);
// this.nodes.button.innerHTML = IconBold;
// return this.nodes.button;
// }
/**
* Create button for Inline Toolbar
*/
public render(): HTMLElement {
this.nodes.button = document.createElement('button') as HTMLButtonElement;
this.nodes.button.type = 'button';
this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier);
this.nodes.button.innerHTML = IconBold;
return this.nodes.button;
public render(): any {
return {
icon: IconBold,
title: 'Bold',
onActivate: () => {
// console.log('clicked, need range');
document.execCommand(this.commandName);
},
};
}
/**
@ -81,7 +96,7 @@ export default class BoldInlineTool implements InlineTool {
public checkState(): boolean {
const isActive = document.queryCommandState(this.commandName);
this.nodes.button.classList.toggle(this.CSS.buttonActive, isActive);
this.nodes.button?.classList.toggle(this.CSS.buttonActive, isActive);
return isActive;
}

View file

@ -128,6 +128,19 @@ export default class LinkInlineTool implements InlineTool {
return this.nodes.button;
}
// /**
// * Create button for Inline Toolbar
// */
// public render(): any {
// return {
// icon: IconLink,
// title: 'Link',
// onActivate: () => {
// // this.surround(this.selection.get());
// },
// };
// }
/**
* Input for the link
*/
@ -135,12 +148,17 @@ export default class LinkInlineTool implements InlineTool {
this.nodes.input = document.createElement('input') as HTMLInputElement;
this.nodes.input.placeholder = this.i18n.t('Add a link');
this.nodes.input.classList.add(this.CSS.input);
// this.nodes.input.classList.add(this.CSS.inputShowed);
this.nodes.input.addEventListener('keydown', (event: KeyboardEvent) => {
if (event.keyCode === this.ENTER_KEY) {
this.enterPressed(event);
}
});
// setTimeout(() => {
// this.nodes.input.focus();
// }, 1000);
return this.nodes.input;
}
@ -150,6 +168,8 @@ export default class LinkInlineTool implements InlineTool {
* @param {Range} range - range to wrap with link
*/
public surround(range: Range): void {
debugger;
console.log('surround');
/**
* Range will be null when user makes second click on the 'link icon' to close opened input
*/
@ -231,6 +251,7 @@ export default class LinkInlineTool implements InlineTool {
* Show/close link input
*/
private toggleActions(): void {
debugger;
if (!this.inputOpened) {
this.openActions(true);
} else {

View file

@ -20,7 +20,8 @@ import UiAPI from './api/ui';
import BlockSettings from './toolbar/blockSettings';
import ConversionToolbar from './toolbar/conversion';
import Toolbar from './toolbar/index';
import InlineToolbar from './toolbar/inline';
// import InlineToolbar from './toolbar/inline';
import InlineToolbar from './toolbar/inline2';
/** . */
import BlockEvents from './blockEvents';
@ -78,4 +79,4 @@ export default {
Saver,
Tools,
UI,
};
};

View file

@ -154,8 +154,8 @@ export default class ConversionToolbar extends Module<ConversionToolbarNodes> {
*/
public close(): void {
this.opened = false;
this.flipper.deactivate();
this.nodes.wrapper.classList.remove(ConversionToolbar.CSS.conversionToolbarShowed);
this.flipper?.deactivate();
this.nodes.wrapper?.classList.remove(ConversionToolbar.CSS.conversionToolbarShowed);
if (_.isFunction(this.togglingCallback)) {
this.togglingCallback(false);

View file

@ -9,7 +9,7 @@ import Shortcuts from '../../utils/shortcuts';
import * as tooltip from '../../utils/tooltip';
import { ModuleConfig } from '../../../types-internal/module-config';
import { CommonInternalSettings } from '../../tools/base';
import { Popover, PopoverEvent, PopoverItemDefaultParams, PopoverItemParams, PopoverItemWithChildrenParams } from '../../utils/popover';
import { Popover, PopoverEvent, PopoverItemDefaultParams, PopoverItemParams, PopoverItemType, PopoverItemWithChildrenParams } from '../../utils/popover';
import { PopoverInline } from '../../utils/popover/popover-inline';
import { getConvertToItems } from '../../utils/blocks';
import { IconReplace } from '@codexteam/icons';
@ -455,7 +455,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
},
});
popoverItems.push({
type: 'separator',
type: PopoverItemType.Separator,
});
}
@ -481,23 +481,47 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
tool.title || _.capitalize(tool.name)
);
if ($.isElement(controlData)) {
htmlElements.push(
this.prepareInlineToolHtml(controlData, instance, toolTitle, shortcutBeautified)
);
} else if (Array.isArray(controlData)) {
const items = (controlData as PopoverItemParams[]).map(item => this.prepareInlineToolItem(item, instance, toolTitle, shortcutBeautified));
[ controlData ].flat().forEach((item) => {
let popoverItem = {
onActivate: (activatedItem: PopoverItemParams) => {
debugger;
this.toolClicked(instance);
},
hint: {
title: toolTitle,
description: shortcutBeautified,
},
isActive: instance.checkState(SelectionUtils.get()),
} as PopoverItemParams;
popoverItems.push(...items);
} else {
popoverItems.push(this.prepareInlineToolItem(controlData, instance, toolTitle, shortcutBeautified));
}
if ($.isElement(item)) {
popoverItem = {
...popoverItem,
type: PopoverItemType.Html,
element: item,
};
} else {
popoverItem = {
...popoverItem,
...(item as PopoverItemParams),
};
}
// if (_.isFunction(instance.renderActions)) {
// const actions = instance.renderActions();
if (_.isFunction(instance.renderActions)) {
const actions = instance.renderActions();
// // this.nodes.actions.appendChild(actions);
// }
popoverItem.children = {
items: [
{
type: PopoverItemType.Html,
element: actions,
},
],
};
}
popoverItems.push(popoverItem);
});
});
return {
@ -517,7 +541,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
private prepareInlineToolItem(item: PopoverItemParams, toolInstance: IInlineTool, toolTitle: string, shortcut: string | undefined): PopoverItemDefaultParams {
const result = {
...item,
onActivate: (activatedItem: PopoverItemParams) => {
onActivate: (activatedItem: PopoverItemParams, event) => {
// @todo proper check
if ('children' in activatedItem) {
return;

View file

@ -43,6 +43,15 @@ interface UINodes {
* @property {Element} nodes.redactor - <ce-redactor>
*/
export default class UI extends Module<UINodes> {
/**
*
* @param params
*/
constructor(params) {
super(params);
// debugger;
}
/**
* Editor.js UI CSS class names
*
@ -97,6 +106,19 @@ export default class UI extends Module<UINodes> {
*/
public isMobile = false;
private _ignoreSelectionChangeEvents = false;
/**
*
* @param value
*/
public ignoreSelectionChangeEvents(value: boolean): void {
console.log('ignoreSelectionChangeEvents', value);
// debugger;
this._ignoreSelectionChangeEvents = value;
// debugger;
}
/**
* Cache for center column rectangle info
* Invalidates on window resize
@ -105,6 +127,7 @@ export default class UI extends Module<UINodes> {
*/
private contentRectCache: DOMRect = undefined;
/**
* Handle window resize only when it finished
*
@ -135,6 +158,7 @@ export default class UI extends Module<UINodes> {
this.loadStyles();
}
/**
* Toggle read-only state
*
@ -234,6 +258,22 @@ export default class UI extends Module<UINodes> {
Toolbar.toolbox.close();
}
/**
*
*/
public enableSelectionChangeEvents(): void {
this.listeners.on(document, 'selectionchange', this.selectionChangeDebounced, true);
}
/**
*
*/
public disableSelectionChangeEvents(): void {
// this.listeners.removeAll();
this.listeners.off(document, 'selectionchange', this.selectionChangeDebounced, true);
}
/**
* Check for mobile mode and save the result
*/
@ -330,6 +370,13 @@ export default class UI extends Module<UINodes> {
$.prepend(document.head, tag);
}
/**
* Handle selection change to manipulate Inline Toolbar appearance
*/
private selectionChangeDebounced = _.debounce(() => {
this.selectionChanged();
}, selectionChangeDebounceTimeout);
/**
* Bind events on the Editor.js interface
*/
@ -363,11 +410,11 @@ export default class UI extends Module<UINodes> {
/**
* Handle selection change to manipulate Inline Toolbar appearance
*/
const selectionChangeDebounced = _.debounce(() => {
this.selectionChanged();
}, selectionChangeDebounceTimeout);
// const selectionChangeDebounced = _.debounce(() => {
// this.selectionChanged();
// }, selectionChangeDebounceTimeout);
this.readOnlyMutableListeners.on(document, 'selectionchange', selectionChangeDebounced, true);
this.listeners.on(document, 'selectionchange', this.selectionChangeDebounced, true);
this.readOnlyMutableListeners.on(window, 'resize', () => {
this.resizeDebouncer();
@ -381,6 +428,7 @@ export default class UI extends Module<UINodes> {
this.watchBlockHoveredEvents();
}
/**
* Listen redactor mousemove to emit 'block-hovered' event
*/

View file

@ -2,7 +2,7 @@ import Dom from '../../../../../dom';
import { IconDotCircle, IconChevronRight } from '@codexteam/icons';
import {
PopoverItemDefaultParams as PopoverItemDefaultParams,
PopoverItemParams as PopoverItemParams,
// PopoverItemParams as PopoverItemParams,
PopoverItemRenderParamsMap,
PopoverItemType
} from '../popover-item.types';
@ -75,7 +75,6 @@ export class PopoverItemDefault extends PopoverItem {
icon: null,
};
/**
* If item is in confirmation state, stores confirmation params such as icon, label, onActivate callback and so on
*/
@ -88,8 +87,8 @@ export class PopoverItemDefault extends PopoverItem {
* @param renderParams - popover item render params.
* The parameters that are not set by user via popover api but rather depend on technical implementation
*/
constructor(private readonly params: PopoverItemDefaultParams, renderParams?: PopoverItemRenderParamsMap[PopoverItemType.Default]) {
super();
constructor(protected readonly params: PopoverItemDefaultParams, renderParams?: PopoverItemRenderParamsMap[PopoverItemType.Default]) {
super(params);
this.nodes.root = this.make(params, renderParams);
}
@ -148,30 +147,30 @@ export class PopoverItemDefault extends PopoverItem {
this.disableSpecialHoverAndFocusBehavior();
}
/**
* Returns item children that are represented as popover items
*/
public get children(): PopoverItemParams[] {
return 'children' in this.params && this.params.children?.items !== undefined ? this.params.children.items : [];
}
// /**
// * Returns item children that are represented as popover items
// */
// public get children(): PopoverItemParams[] {
// return 'children' in this.params && this.params.children?.items !== undefined ? this.params.children.items : [];
// }
/**
* Returns item children that are represented as custom HTML
*/
public get childrenHTML(): HTMLElement | undefined {
if (!('children' in this.params)) {
return undefined;
}
// /**
// * Returns item children that are represented as custom HTML
// */
// public get childrenHTML(): HTMLElement | undefined {
// if (!('children' in this.params)) {
// return undefined;
// }
return this.params.children?.customHtml;
}
// return this.params.children?.customHtml;
// }
/**
* Returns true if item has any type of children
*/
public get hasChildren(): boolean {
return this.children.length > 0 || this.childrenHTML !== undefined;
}
// /**
// * Returns true if item has any type of children
// */
// public get hasChildren(): boolean {
// return this.children.length > 0; // || this.childrenHTML !== undefined;
// }
/**
* Returns true if item has children that should be searchable

View file

@ -20,7 +20,7 @@ export class PopoverItemHtml extends PopoverItem {
* The parameters that are not set by user via popover api but rather depend on technical implementation
*/
constructor(params: PopoverItemHtmlParams, renderParams?: PopoverItemRenderParamsMap[PopoverItemType.Html]) {
super();
super(params);
this.nodes = {
root: Dom.make('div', css.root),
@ -63,4 +63,12 @@ export class PopoverItemHtml extends PopoverItem {
return Array.from(controls);
}
/**
*
*/
public handleClick(): void {
// debugger;
this.params.onActivate?.(this.params);
}
}

View file

@ -1,10 +1,18 @@
import * as tooltip from '../../../../utils/tooltip';
import { type HintPosition, Hint } from '../hint';
import { PopoverItemParams } from './popover-item.types';
/**
* Popover item abstract class
*/
export abstract class PopoverItem {
/**
* Constructs the instance
*
* @param params - instance parameters
*/
constructor(protected readonly params?: PopoverItemParams) {}
/**
* Adds hint to the item element if hint data is provided
*
@ -31,4 +39,18 @@ export abstract class PopoverItem {
* @param isHidden - true if item should be hidden
*/
public abstract toggleHidden(isHidden: boolean): void;
/**
* Returns item children that are represented as popover items
*/
public get children(): PopoverItemParams[] {
return 'children' in this.params && this.params.children?.items !== undefined ? this.params.children.items : [];
}
/**
* Returns true if item has any type of children
*/
public get hasChildren(): boolean {
return this.children.length > 0;
}
}

View file

@ -14,6 +14,21 @@ export enum PopoverItemType {
Html = 'html'
}
/**
* Represents popover item children configuration
*/
export interface PopoverItemChildren {
/**
* True if children items should be searchable
*/
searchable?: boolean;
/**
* Items of nested popover that should be open on the current item hover/click (depending on platform)
*/
items?: PopoverItemParams[];
}
/**
* Represents popover item separator.
* Special item type that is used to separate items in the popover.
@ -45,6 +60,10 @@ export interface PopoverItemHtmlParams {
hint?: HintParams;
}
export interface PopoverItemHtmlWithChildrenParams extends PopoverItemHtmlParams{
children?: PopoverItemChildren;
}
/**
* Common parameters for all kinds of default popover items: with or without confirmation
*/
@ -131,7 +150,6 @@ export interface PopoverItemWithoutConfirmationParams extends PopoverItemDefault
onActivate: (item: PopoverItemParams, event?: PointerEvent) => void;
}
/**
* Represents popover item with children (nested popover items)
*/
@ -142,16 +160,7 @@ export interface PopoverItemWithChildrenParams extends PopoverItemDefaultBasePar
/**
* Items of nested popover that should be open on the current item hover/click (depending on platform)
*/
children?: {
/**
* True if children items should be searchable
*/
searchable?: boolean;
items?: PopoverItemParams[];
customHtml?: HTMLElement;
}
children?: PopoverItemChildren
}
/**
@ -168,7 +177,8 @@ export type PopoverItemDefaultParams =
export type PopoverItemParams =
PopoverItemDefaultParams |
PopoverItemSeparatorParams |
PopoverItemHtmlParams;
PopoverItemHtmlParams |
PopoverItemHtmlWithChildrenParams;
/**

View file

@ -174,7 +174,7 @@ export abstract class PopoverAbstract<Nodes extends PopoverNodes = PopoverNodes>
* @param event - event to retrieve popover item from
*/
protected getTargetItem(event: Event): PopoverItemDefault | undefined {
return this.itemsDefault.find(el => {
return this.items.find(el => {
const itemEl = el.getElement();
if (itemEl === null) {
@ -248,6 +248,8 @@ export abstract class PopoverAbstract<Nodes extends PopoverNodes = PopoverNodes>
if (item.hasChildren) {
this.showNestedItems(item);
item.handleClick();
return;
}

View file

@ -52,6 +52,7 @@ export class PopoverDesktop extends PopoverAbstract {
*/
constructor(params: PopoverParams) {
super(params);
console.log(params.items);
if (params.nestingLevel !== undefined) {
this.nestingLevel = params.nestingLevel;

View file

@ -97,7 +97,7 @@
border-radius: 0 0 4px 4px;
margin: 0;
font-size: 13px;
padding: 10px;
/* padding: 10px; */
width: 100%;
box-sizing: border-box;
display: none;