Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(inline-toolbar): inline tools now can be used in the readonly mode #2832

Merged
merged 3 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# Changelog

### 2.31.0

- `New` - Inline tools (those with `isReadOnlySupported` specified) can now be used in read-only mode
- `Fix` - Fix selection of first block in read-only initialization with "autofocus=true"

### 2.30.6

`Fix` – Fix the display of ‘Convert To’ near blocks that do not have the ‘conversionConfig.export’ rule specified
`Fix` – The Plus button does not appear when the editor is loaded in an iframe in Chrome
- `Fix` – Fix the display of ‘Convert To’ near blocks that do not have the ‘conversionConfig.export’ rule specified
- `Fix` – The Plus button does not appear when the editor is loaded in an iframe in Chrome
- `Fix` - Prevent inline toolbar from closing in nested instance of editor

### 2.30.5
Expand Down Expand Up @@ -33,7 +38,7 @@
- `New` – Block Tunes now supports nesting items
- `New` – Block Tunes now supports separator items
- `New` – *Menu Config* – New item type – HTML
`New` – *Menu Config* – Default and HTML items now support hints
- `New` – *Menu Config* – Default and HTML items now support hints
- `New` – Inline Toolbar has new look 💅
- `New` – Inline Tool's `render()` now supports [Menu Config](https://editorjs.io/menu-config/) format
- `New` – *ToolsAPI* – All installed block tools now accessible via ToolsAPI `getBlockTools()` method
Expand Down
2 changes: 1 addition & 1 deletion src/components/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default class Core {
UI.checkEmptiness();
ModificationsObserver.enable();

if ((this.configuration as EditorConfig).autofocus) {
if ((this.configuration as EditorConfig).autofocus === true && this.configuration.readOnly !== true) {
Caret.setToBlock(BlockManager.blocks[0], Caret.positions.START);
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/modules/blockManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ export default class BlockManager extends Module {
*
* @param {Node} element - html element to get Block by
*/
public getBlock(element: HTMLElement): Block {
public getBlock(element: HTMLElement): Block | undefined {
if (!$.isElement(element) as boolean) {
element = element.parentNode as HTMLElement;
}
Expand Down
140 changes: 88 additions & 52 deletions src/components/modules/toolbar/inline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { CommonInternalSettings } from '../../tools/base';
import type { Popover, PopoverItemHtmlParams, PopoverItemParams, WithChildren } from '../../utils/popover';
import { PopoverItemType } from '../../utils/popover';
import { PopoverInline } from '../../utils/popover/popover-inline';
import type InlineToolAdapter from 'src/components/tools/inline';

/**
* Inline Toolbar elements
Expand Down Expand Up @@ -54,7 +55,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
/**
* Currently visible tools instances
*/
private toolsInstances: Map<string, IInlineTool> | null = new Map();
private tools: Map<InlineToolAdapter, IInlineTool> = new Map();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WeakMap maybe?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can't iterate over WeakMap


/**
* @param moduleConfiguration - Module Configuration
Expand All @@ -66,21 +67,10 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
config,
eventsDispatcher,
});
}

/**
* Toggles read-only mode
*
* @param {boolean} readOnlyEnabled - read-only mode
*/
public toggleReadOnly(readOnlyEnabled: boolean): void {
if (!readOnlyEnabled) {
window.requestIdleCallback(() => {
this.make();
}, { timeout: 2000 });
} else {
this.destroy();
}
window.requestIdleCallback(() => {
this.make();
}, { timeout: 2000 });
}

/**
Expand Down Expand Up @@ -116,14 +106,10 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
return;
}

if (this.Editor.ReadOnly.isEnabled) {
return;
}

Array.from(this.toolsInstances.entries()).forEach(([name, toolInstance]) => {
const shortcut = this.getToolShortcut(name);
for (const [tool, toolInstance] of this.tools) {
const shortcut = this.getToolShortcut(tool.name);

if (shortcut) {
if (shortcut !== undefined) {
Shortcuts.remove(this.Editor.UI.nodes.redactor, shortcut);
}

Expand All @@ -133,9 +119,9 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
if (_.isFunction(toolInstance.clear)) {
toolInstance.clear();
}
});
}

this.toolsInstances = null;
this.tools = new Map();

this.reset();
this.opened = false;
Expand Down Expand Up @@ -204,10 +190,12 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
this.popover.destroy();
}

const inlineTools = await this.getInlineTools();
this.createToolsInstances();

const popoverItems = await this.getPopoverItems();

this.popover = new PopoverInline({
items: inlineTools,
items: popoverItems,
scopeElement: this.Editor.API.methods.ui.nodes.redactor,
messages: {
nothingFound: I18n.ui(I18nInternalNS.ui.popover, 'Nothing found'),
Expand Down Expand Up @@ -290,25 +278,36 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
return false;
}

if (currentSelection && tagsConflictsWithSelection.includes(target.tagName)) {
if (currentSelection !== null && tagsConflictsWithSelection.includes(target.tagName)) {
return false;
}

// The selection of the element only in contenteditable
const contenteditable = target.closest('[contenteditable="true"]');
/**
* Check if there is at leas one tool enabled by current Block's Tool
*/
const currentBlock = this.Editor.BlockManager.getBlock(currentSelection.anchorNode as HTMLElement);

if (contenteditable === null) {
if (!currentBlock) {
return false;
}

// is enabled by current Block's Tool
const currentBlock = this.Editor.BlockManager.getBlock(currentSelection.anchorNode as HTMLElement);
/**
* Check that at least one tool is available for the current block
*/
const toolsAvailable = this.getTools();
const isAtLeastOneToolAvailable = toolsAvailable.some((tool) => currentBlock.tool.inlineTools.has(tool.name));

if (!currentBlock) {
if (isAtLeastOneToolAvailable === false) {
return false;
}

return currentBlock.tool.inlineTools.size !== 0;
/**
* Inline toolbar will be shown only if the target is contenteditable
* In Read-Only mode, the target should be contenteditable with "false" value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have this documented somewhere?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not yet, it's the new behavior

*/
const contenteditable = target.closest('[contenteditable]');

return contenteditable !== null;
}

/**
Expand All @@ -317,32 +316,63 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
*/

/**
* Returns Inline Tools segregated by their appearance type: popover items and custom html elements.
* Sets this.toolsInstances map
* Returns tools that are available for current block
*
* Used to check if Inline Toolbar could be shown
* and to render tools in the Inline Toolbar
*/
private async getInlineTools(): Promise<PopoverItemParams[]> {
const currentSelection = SelectionUtils.get();
const currentBlock = this.Editor.BlockManager.getBlock(currentSelection.anchorNode as HTMLElement);
private getTools(): InlineToolAdapter[] {
const currentBlock = this.Editor.BlockManager.currentBlock;

if (!currentBlock) {
return [];
}

const inlineTools = Array.from(currentBlock.tool.inlineTools.values());

const popoverItems = [] as PopoverItemParams[];
return inlineTools.filter((tool) => {
/**
* We support inline tools in read only mode.
* Such tools should have isReadOnlySupported flag set to true
*/
if (this.Editor.ReadOnly.isEnabled && tool.isReadOnlySupported !== true) {
return false;
}

if (this.toolsInstances === null) {
this.toolsInstances = new Map();
}
return true;
});
}

/**
* Constructs tools instances and saves them to this.tools
*/
private createToolsInstances(): void {
this.tools = new Map();

for (let i = 0; i < inlineTools.length; i++) {
const tool = inlineTools[i];
const tools = this.getTools();

tools.forEach((tool) => {
const instance = tool.create();
const renderedTool = await instance.render();

this.toolsInstances.set(tool.name, instance);
this.tools.set(tool, instance);
});
}

/**
* Returns Popover Items for tools segregated by their appearance type: regular items and custom html elements.
*/
private async getPopoverItems(): Promise<PopoverItemParams[]> {
const popoverItems = [] as PopoverItemParams[];

let i = 0;

for (const [tool, instance] of this.tools) {
const renderedTool = await instance.render();

/** Enable tool shortcut */
const shortcut = this.getToolShortcut(tool.name);

if (shortcut) {
if (shortcut !== undefined) {
try {
this.enableShortcuts(tool.name, shortcut);
} catch (e) {}
Expand Down Expand Up @@ -429,7 +459,9 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
type: PopoverItemType.Default,
} as PopoverItemParams;

/** Prepend with separator if item has children and not the first one */
/**
* Prepend the separator if item has children and not the first one
*/
if ('children' in popoverItem && i !== 0) {
popoverItems.push({
type: PopoverItemType.Separator,
Expand All @@ -438,14 +470,18 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {

popoverItems.push(popoverItem);

/** Append separator after the item is it has children and not the last one */
if ('children' in popoverItem && i < inlineTools.length - 1) {
/**
* Append a separator after the item if it has children and not the last one
*/
if ('children' in popoverItem && i < this.tools.size - 1) {
popoverItems.push({
type: PopoverItemType.Separator,
});
}
}
});

i++;
}

return popoverItems;
Expand Down Expand Up @@ -533,7 +569,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
* Check Tools` state by selection
*/
private checkToolsState(): void {
this.toolsInstances?.forEach((toolInstance) => {
this.tools?.forEach((toolInstance) => {
toolInstance.checkState?.(SelectionUtils.get());
});
}
Expand Down
Loading
Loading