Skip to content

feat(inline-tools): Inline tools rendered as popover #2718

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

Merged
merged 128 commits into from
Jun 30, 2024
Merged
Show file tree
Hide file tree
Changes from 118 commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
392ff54
Add custom item
TatianaFomina May 4, 2024
0d506c2
Remove customcontent parameter from popover
TatianaFomina May 4, 2024
bf3bffb
Tests
TatianaFomina May 4, 2024
d93cdea
Cleanup
TatianaFomina May 4, 2024
6fe4b44
Cleanup
TatianaFomina May 4, 2024
a70eeae
Lint
TatianaFomina May 4, 2024
f398168
Cleanup
TatianaFomina May 4, 2024
af24b9c
Rename custom to html, add enum with item types
TatianaFomina May 4, 2024
005e170
Fix tests
TatianaFomina May 4, 2024
3ed7f7e
Support hint
TatianaFomina May 4, 2024
b713e63
Merge branch 'next' into feat/popover-hint
TatianaFomina May 4, 2024
26053ad
Rename hint content to hint
TatianaFomina May 4, 2024
9b30fac
Align hint left
TatianaFomina May 4, 2024
34e3f1e
Move types and exports
TatianaFomina May 4, 2024
04f2851
Merge branch 'next' into feat/hint-2
TatianaFomina May 4, 2024
b0b2e29
Update changelog
TatianaFomina May 4, 2024
7f053d9
Cleanup
TatianaFomina May 4, 2024
17982a6
Add todos
TatianaFomina May 4, 2024
5c7415c
Change the way hint is disabled for mobile
TatianaFomina May 4, 2024
7e90001
Get rid of buildItems override
TatianaFomina May 11, 2024
4daf8f9
Update comment
TatianaFomina May 11, 2024
53a6cbd
tmp
TatianaFomina Apr 30, 2024
95685db
Positioning
TatianaFomina Apr 30, 2024
65e30e9
Add convert to
TatianaFomina Apr 30, 2024
0f34f0f
Make conversion menu searchable
TatianaFomina Apr 30, 2024
6383acf
Toggling actions
TatianaFomina May 1, 2024
ecc8204
Cleanup
TatianaFomina May 1, 2024
1a1120a
Progress
TatianaFomina May 11, 2024
7b67565
Cleanup
TatianaFomina May 11, 2024
10ef904
Progress
TatianaFomina May 11, 2024
4d7befc
Merge branch 'next' into feat/popover-horizontal
TatianaFomina May 17, 2024
acb5d82
Hint alignment
TatianaFomina May 17, 2024
1700b62
Hide tooltip on popover close
TatianaFomina May 17, 2024
adf1bec
Button sizes
TatianaFomina May 18, 2024
7a6b361
Fix positioning
TatianaFomina May 18, 2024
adadfe8
Remove info about conversion toolbar
TatianaFomina May 18, 2024
5a8d23d
Fix ts/eslint warnings
TatianaFomina May 18, 2024
16e0560
Fix closeOnActivate for nested popover
TatianaFomina May 18, 2024
e5e0a5d
Remove conversion styles
TatianaFomina May 18, 2024
e945a01
Cleanup
TatianaFomina May 18, 2024
a86faf4
Conversion label
TatianaFomina May 18, 2024
c5899e7
Close nested popover on outclick
TatianaFomina May 22, 2024
cf768c2
Close nested popover on other item click
TatianaFomina May 23, 2024
80f930f
Fix invisible nested popover on mobile
TatianaFomina May 23, 2024
f5ce3b8
Styles
TatianaFomina May 25, 2024
dd22923
Fix selection problem on nested popover open
TatianaFomina May 25, 2024
eeb4271
Comment
TatianaFomina May 25, 2024
76d4ecc
Styles updates
TatianaFomina May 30, 2024
0d34bce
Fix selection issue
TatianaFomina May 30, 2024
72bd1e9
Merge branch 'next' into feat/popover-horizontal
TatianaFomina May 30, 2024
fd45af8
Lint
TatianaFomina May 30, 2024
ed18292
Update bold tool
TatianaFomina May 30, 2024
fc64f13
Fix sanitize test
TatianaFomina May 30, 2024
075f6f8
Fix link tool shortcut
TatianaFomina May 31, 2024
73eb18a
Lint
TatianaFomina May 31, 2024
8a14940
Fix link bug
TatianaFomina May 31, 2024
ef9c169
Fix inline toolbar tests
TatianaFomina May 31, 2024
e50c437
Fix safari bug
TatianaFomina Jun 8, 2024
134ec7b
Add convert to hint
TatianaFomina Jun 8, 2024
af58965
Fix icon sizes
TatianaFomina Jun 8, 2024
6752f9c
Unify active state
TatianaFomina Jun 8, 2024
f8a93aa
Fix tests
TatianaFomina Jun 8, 2024
8ae5465
Change inline tools order for header in index.html
TatianaFomina Jun 8, 2024
8700f84
Rewrite getConvertToItems util with editor api
TatianaFomina Jun 8, 2024
834391c
Rename
TatianaFomina Jun 8, 2024
f71a4a3
Lint
TatianaFomina Jun 8, 2024
d5a8ed0
Fix colors in mobile safari
TatianaFomina Jun 8, 2024
c1e9807
Hide hint on mobile for inline popover
TatianaFomina Jun 8, 2024
d28c66e
Revert "Rewrite getConvertToItems util with editor api"
TatianaFomina Jun 8, 2024
bde339f
Revert "Rename"
TatianaFomina Jun 8, 2024
aa17a98
Move getConvertToItems() to conversion module
TatianaFomina Jun 8, 2024
52b0bb4
Update comment
TatianaFomina Jun 8, 2024
d9da2fc
Use const
TatianaFomina Jun 9, 2024
b88e184
Add some tests
TatianaFomina Jun 9, 2024
fd855cf
Update changelog
TatianaFomina Jun 9, 2024
8e83f0e
Replace div with button in popover-item-default
TatianaFomina Jun 12, 2024
19e94b9
Fix import
TatianaFomina Jun 12, 2024
dfa849d
Use isToolConvertable
TatianaFomina Jun 12, 2024
38776c0
fixup! Replace div with button in popover-item-default
TatianaFomina Jun 12, 2024
840532f
fixup! Replace div with button in popover-item-default
TatianaFomina Jun 15, 2024
b4b9a9a
Lint
TatianaFomina Jun 15, 2024
4432e5a
Update types
TatianaFomina Jun 15, 2024
24e54b5
Better renderActions handling
TatianaFomina Jun 15, 2024
37ccf2f
fixup! Update types
TatianaFomina Jun 15, 2024
29dc873
Lint
TatianaFomina Jun 15, 2024
407fa3f
Remove getActiveToolboxEntryOfBlock
TatianaFomina Jun 15, 2024
0128650
Call querySelector on popoverEl instead of document
TatianaFomina Jun 15, 2024
ace83f1
Clarify hasAutoFocusableElements
TatianaFomina Jun 16, 2024
6312177
rename setTriggerItemPositionProperty
TatianaFomina Jun 16, 2024
97e1f7a
Add comment about nesting level
TatianaFomina Jun 16, 2024
18f5899
Move adding css class .ce-popover--nested-level-1 to inline popover
TatianaFomina Jun 16, 2024
6d535fa
add destroy for search
TatianaFomina Jun 16, 2024
960c8fa
Fix comments
TatianaFomina Jun 16, 2024
84dcad9
Rename events
TatianaFomina Jun 16, 2024
9fc2221
Rename isExpanded to isOpen
TatianaFomina Jun 27, 2024
7543506
Fix comment
TatianaFomina Jun 27, 2024
c8b2fde
Replace comment with return
TatianaFomina Jun 27, 2024
76261fd
Rename events
TatianaFomina Jun 27, 2024
eb77804
Deprecate TunesMenuConfig
TatianaFomina Jun 27, 2024
848a49f
Update tools
TatianaFomina Jun 27, 2024
dd39c61
Fixes
TatianaFomina Jun 29, 2024
66ebe0f
Convert to as tool
TatianaFomina Jun 29, 2024
be27d82
Save selection inside conversion tool
TatianaFomina Jun 29, 2024
f60624a
Remove redundant events
TatianaFomina Jun 29, 2024
d8e3621
Force add convertTo
TatianaFomina Jun 29, 2024
a67cc10
Remove hardcoded conversion menu
TatianaFomina Jun 29, 2024
9e1f822
Fix for mobile
TatianaFomina Jun 29, 2024
9a992d7
Fix padding
TatianaFomina Jun 29, 2024
4b5f980
Merge branch 'next' into feat/popover-horizontal
TatianaFomina Jun 29, 2024
b7ca60e
Clear toolsInstances
TatianaFomina Jun 29, 2024
81e6584
Add activateItemByName()
TatianaFomina Jun 29, 2024
877193a
Deprecate Inline tool's surround and checkState
TatianaFomina Jun 29, 2024
f69bf96
Lint
TatianaFomina Jun 29, 2024
460da98
Remove getCurrentSelection API method
TatianaFomina Jun 30, 2024
9e0e837
Use getConvertibleToolsForBlock fn
TatianaFomina Jun 30, 2024
49b85ea
Use getConvertibleToolsForBlock in Block Tunes
TatianaFomina Jun 30, 2024
e67ff5c
Remove Conversion module
TatianaFomina Jun 30, 2024
fdeef79
Return TunesMenuConfigItem type
TatianaFomina Jun 30, 2024
603e177
Clarify comment
TatianaFomina Jun 30, 2024
b2a02f8
Use block.save() instead of data
TatianaFomina Jun 30, 2024
84287c1
Fix exported block api
TatianaFomina Jun 30, 2024
8cb10ab
Add inline toolbar tests and lint
TatianaFomina Jun 30, 2024
07347ed
Update changelog
TatianaFomina Jun 30, 2024
872b012
Fix tests
TatianaFomina Jun 30, 2024
0e1774e
Lint
TatianaFomina Jun 30, 2024
45a5b63
Clarify comments
TatianaFomina Jun 30, 2024
d875ebc
Align left
TatianaFomina Jun 30, 2024
9dc3fdb
Update input style
TatianaFomina Jun 30, 2024
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
2 changes: 2 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
- `New` – *Menu Config* – New item type – HTML
– `Refactoring` – Switched to Vite as Cypress bundler
– `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
– `Fix` — Deleting whitespaces at the start/end of the block
– `Improvement` — *Types* — `BlockToolConstructorOptions` type improved, `block` and `config` are not optional anymore

Expand Down
2 changes: 1 addition & 1 deletion example/tools/code
2 changes: 1 addition & 1 deletion example/tools/image
2 changes: 1 addition & 1 deletion example/tools/inline-code
2 changes: 1 addition & 1 deletion example/tools/link
2 changes: 1 addition & 1 deletion example/tools/list
Submodule list updated from a6dc6a to f0e9f0
2 changes: 1 addition & 1 deletion example/tools/marker
2 changes: 1 addition & 1 deletion example/tools/nested-list
2 changes: 1 addition & 1 deletion example/tools/quote
2 changes: 1 addition & 1 deletion example/tools/raw
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
*/
header: {
class: Header,
inlineToolbar: ['marker', 'link'],
inlineToolbar: ['link', 'marker'],
config: {
placeholder: 'Header'
},
Expand Down
4 changes: 2 additions & 2 deletions src/components/block-tunes/block-tune-delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
import { API, BlockTune } from '../../../types';
import { IconCross } from '@codexteam/icons';
import { TunesMenuConfig } from '../../../types/tools';
import { MenuConfig } from '../../../types/tools/menu-config';

/**
*
Expand Down Expand Up @@ -35,7 +35,7 @@ export default class DeleteTune implements BlockTune {
/**
* Tune's appearance in block settings menu
*/
public render(): TunesMenuConfig {
public render(): MenuConfig {
return {
icon: IconCross,
title: this.api.i18n.t('Delete'),
Expand Down
17 changes: 16 additions & 1 deletion src/components/block/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Block from './index';
import { BlockToolData, ToolConfig } from '../../../types/tools';
import { BlockToolData, ToolConfig, ToolboxConfigEntry } from '../../../types/tools';
import { SavedData } from '../../../types/data-formats';
import { BlockAPI as BlockAPIInterface } from '../../../types/api';

Expand Down Expand Up @@ -39,6 +39,13 @@ function BlockAPI(
return block.config;
},

/**
* Tool's data
*/
get data(): Promise<BlockToolData> {
return block.data;
},

/**
* .ce-block element, that wraps plugin contents
*
Expand Down Expand Up @@ -128,6 +135,14 @@ function BlockAPI(
dispatchChange(): void {
block.dispatchChange();
},

/**
* Tool could specify several entries to be displayed at the Toolbox (for example, "Heading 1", "Heading 2", "Heading 3")
* This method returns the entry that is related to the Block (depended on the Block data)
*/
getActiveToolboxEntry(): Promise<ToolboxConfigEntry | undefined> {
return block.getActiveToolboxEntry();
},
};

Object.setPrototypeOf(this, blockAPI);
Expand Down
53 changes: 10 additions & 43 deletions src/components/inline-tools/inline-tool-bold.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { InlineTool, SanitizerConfig } from '../../../types';
import { IconBold } from '@codexteam/icons';
import { MenuConfig } from '../../../types/tools';

/**
* Bold Tool
Expand Down Expand Up @@ -38,52 +39,18 @@ export default class BoldInlineTool implements InlineTool {
*/
private readonly commandName: string = 'bold';

/**
* Styles
*/
private readonly CSS = {
button: 'ce-inline-tool',
buttonActive: 'ce-inline-tool--active',
buttonModifier: 'ce-inline-tool--bold',
};

/**
* Elements
*/
private nodes: {button: HTMLButtonElement} = {
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;
}

/**
* Wrap range with <b> tag
*/
public surround(): void {
document.execCommand(this.commandName);
}

/**
* Check selection and set activated state to button if there are <b> tag
*
* @returns {boolean}
*/
public checkState(): boolean {
const isActive = document.queryCommandState(this.commandName);

this.nodes.button.classList.toggle(this.CSS.buttonActive, isActive);

return isActive;
public render(): MenuConfig {
return {
icon: IconBold,
name: 'bold',
onActivate: () => {
document.execCommand(this.commandName);
},
isActive: () => document.queryCommandState(this.commandName),
};
}

/**
Expand Down
113 changes: 113 additions & 0 deletions src/components/inline-tools/inline-tool-convert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { IconReplace } from '@codexteam/icons';
import { InlineTool, API } from '../../../types';
import { MenuConfig } from '../../../types/tools';
import * as _ from '../utils';
import { Blocks, Selection, Tools, I18n, Caret } from '../../../types/api';
import SelectionUtils from '../selection';
import { getConvertibleToolsForBlock } from '../utils/blocks';

/**
* Inline tools for converting blocks
*/
export default class ConvertInlineTool implements InlineTool {
/**
* Specifies Tool as Inline Toolbar Tool
*/
public static isInline = true;

/**
* API for working with editor blocks
*/
private readonly blocksAPI: Blocks;

/**
* API for working with Selection
*/
private readonly selectionAPI: Selection;

/**
* API for working with Tools
*/
private readonly toolsAPI: Tools;

/**
* I18n API
*/
private readonly i18nAPI: I18n;

/**
* API for working with Caret
*/
private readonly caretAPI: Caret;

/**
* @param api - Editor.js API
*/
constructor({ api }: { api: API }) {
this.i18nAPI = api.i18n;
this.blocksAPI = api.blocks;
this.selectionAPI = api.selection;
this.toolsAPI = api.tools;
this.caretAPI = api.caret;
}

/**
* Returns tool's UI config
*/
public async render(): Promise<MenuConfig> {
const currentSelection = SelectionUtils.get();
const currentBlock = this.blocksAPI.getBlockByElement(currentSelection.anchorNode as HTMLElement);
const allBlockTools = this.toolsAPI.getBlockTools();
const convertibleTools = await getConvertibleToolsForBlock(currentBlock, allBlockTools);

if (convertibleTools.length === 0) {
return [];
}

const convertToItems = convertibleTools.reduce((result, tool) => {
tool.toolbox.forEach((toolboxItem) => {
result.push({
icon: toolboxItem.icon,
title: toolboxItem.title,
name: tool.name,
closeOnActivate: true,
onActivate: async () => {
const newBlock = await this.blocksAPI.convert(currentBlock.id, tool.name, toolboxItem.data);

this.caretAPI.setToBlock(newBlock, 'end');
},
});
});

return result;
}, []);

const currentBlockToolboxItem = await currentBlock.getActiveToolboxEntry();
const icon = currentBlockToolboxItem !== undefined ? currentBlockToolboxItem.icon : IconReplace;
const isDesktop = !_.isMobileScreen();

return {
icon,
name: 'convert-to',
hint: {
title: this.i18nAPI.t('Convert to'),
},
children: {
searchable: isDesktop,
items: convertToItems,
onOpen: () => {
if (isDesktop) {
this.selectionAPI.setFakeBackground();
this.selectionAPI.save();
}
},
onClose: () => {
if (isDesktop) {
this.selectionAPI.restore();
this.selectionAPI.removeFakeBackground();
}
},
},
};
}
}
18 changes: 18 additions & 0 deletions src/components/modules/api/blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default class BlocksAPI extends Module {
getCurrentBlockIndex: (): number => this.getCurrentBlockIndex(),
getBlockIndex: (id: string): number => this.getBlockIndex(id),
getBlocksCount: (): number => this.getBlocksCount(),
getBlockByElement: (element: HTMLElement) => this.getBlockByElement(element),
stretchBlock: (index: number, status = true): void => this.stretchBlock(index, status),
insertNewBlock: (): void => this.insertNewBlock(),
insert: this.insert,
Expand Down Expand Up @@ -108,6 +109,23 @@ export default class BlocksAPI extends Module {
return new BlockAPI(block);
}

/**
* Get Block API object by html element
*
* @param element - html element to get Block by
*/
public getBlockByElement(element: HTMLElement): BlockAPIInterface | undefined {
const block = this.Editor.BlockManager.getBlock(element);

if (block === undefined) {
_.logLabeled('There is no block corresponding to element `' + element + '`', 'warn');

return;
}

return new BlockAPI(block);
}

/**
* Call Block Manager method that swap Blocks
*
Expand Down
1 change: 1 addition & 0 deletions src/components/modules/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default class API extends Module {
return {
blocks: this.Editor.BlocksAPI.methods,
caret: this.Editor.CaretAPI.methods,
tools: this.Editor.ToolsAPI.methods,
events: this.Editor.EventsAPI.methods,
listeners: this.Editor.ListenersAPI.methods,
notifier: this.Editor.NotifierAPI.methods,
Expand Down
13 changes: 11 additions & 2 deletions src/components/modules/api/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import Module from '../../__module';
* Provides with methods working with SelectionUtils
*/
export default class SelectionAPI extends Module {
/**
* Global SelectionUtils instance
*/
private selectionUtils = new SelectionUtils();

/**
* Available methods
*
Expand All @@ -16,6 +21,10 @@ export default class SelectionAPI extends Module {
return {
findParentTag: (tagName: string, className?: string): HTMLElement | null => this.findParentTag(tagName, className),
expandToTag: (node: HTMLElement): void => this.expandToTag(node),
save: () => this.selectionUtils.save(),
restore: () => this.selectionUtils.restore(),
setFakeBackground: () => this.selectionUtils.setFakeBackground(),
removeFakeBackground: () => this.selectionUtils.removeFakeBackground(),
};
}

Expand All @@ -27,7 +36,7 @@ export default class SelectionAPI extends Module {
* @returns {HTMLElement|null}
*/
public findParentTag(tagName: string, className?: string): HTMLElement | null {
return new SelectionUtils().findParentTag(tagName, className);
return this.selectionUtils.findParentTag(tagName, className);
}

/**
Expand All @@ -36,6 +45,6 @@ export default class SelectionAPI extends Module {
* @param {HTMLElement} node - tag that should contain selection
*/
public expandToTag(node: HTMLElement): void {
new SelectionUtils().expandToTag(node);
this.selectionUtils.expandToTag(node);
}
}
16 changes: 16 additions & 0 deletions src/components/modules/api/tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Tools as ToolsAPIInterface } from '../../../../types/api';
import Module from '../../__module';

/**
* Provides methods for accessing installed Editor tools
*/
export default class ToolsAPI extends Module {
/**
* Available methods
*/
public get methods(): ToolsAPIInterface {
return {
getBlockTools: () => Array.from(this.Editor.Tools.blockTools.values()),
};
}
}
Loading
Loading