-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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(); | ||
|
||
/** | ||
* @param moduleConfiguration - Module Configuration | ||
|
@@ -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 }); | ||
} | ||
|
||
/** | ||
|
@@ -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); | ||
} | ||
|
||
|
@@ -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; | ||
|
@@ -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'), | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have this documented somewhere? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
} | ||
|
||
/** | ||
|
@@ -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) {} | ||
|
@@ -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, | ||
|
@@ -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; | ||
|
@@ -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()); | ||
}); | ||
} | ||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WeakMap maybe?
There was a problem hiding this comment.
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