Skip to content

Commit

Permalink
feat(dom-adapters): basic inline tool adapter implementation (#74)
Browse files Browse the repository at this point in the history
* Implmenet global CaretAdapter

* Handle native inputs

* Pass input type to Input component props

* Use class to represent index

* Fix lint in dom-adapters

* fix linter

* added inline tool adapter

* implement model updates

* lint fix

* fix index

* adapter renders inline tools

* lint fix and clean up

* jsdoc

* clean up

* jsdoc

* jsdoc

* surround content replaced

* suggestions

* lint fix

* jsdoc

* added bold and italic inline tools into core package

* naming

* naming

* added inline toolbar and inlineToolAdapter init into core

* update packages and lock

* build fix

* implement inline tool adapter to core

- fully implement current realization of inline tool adapter to core
- remove from the playground

* clean up

* jsdoc and naming improvements

* naming

* naming

* renaming

* fix hardcoded

* tools are initialized inside of the inline toolbar initialization

* fixed inline tool attaching

* jsdoc

* naming fix

* fixed imports

* lint fix

* try build fix

* install dependencies

* add sdk package

* fix build for core

* change package name in actions

* add references

* typo

* fix build

---------

Co-authored-by: George Berezhnoy <gohabereg@users.noreply.github.com>
Co-authored-by: George Berezhnoy <gohabereg@gmail.com>
  • Loading branch information
3 people authored Aug 29, 2024
1 parent 884d0ca commit 50f0db4
Show file tree
Hide file tree
Showing 37 changed files with 816 additions and 43 deletions.
4 changes: 3 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
"typescript": "^5.5.4"
},
"dependencies": {
"@editorjs/dom": "^1.0.0",
"@editorjs/dom-adapters": "workspace:^",
"@editorjs/editorjs": "^2.30.5",
"@editorjs/model": "workspace:^"
"@editorjs/model": "workspace:^",
"@editorjs/sdk": "workspace:^"
}
}
2 changes: 2 additions & 0 deletions packages/core/src/entities/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './BlockTool.js';
export * from './Config.js';
23 changes: 22 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { BlockAddedEvent, EditorJSModel, EventType } from '@editorjs/model';
import type { CoreConfig, CoreConfigValidated } from './entities/Config.js';
import { composeDataFromVersion2 } from './utils/composeDataFromVersion2.js';
import ToolsManager from './tools/ToolsManager.js';
import { BlockToolAdapter, CaretAdapter } from '@editorjs/dom-adapters';
import { BlockToolAdapter, CaretAdapter, InlineToolsAdapter } from '@editorjs/dom-adapters';
import type { BlockAPI, BlockToolData, API as EditorjsApi, ToolConfig } from '@editorjs/editorjs';
import type { BlockTool } from './entities/BlockTool.js';
import { InlineToolbar } from './ui/InlineToolbar/index.js';

/**
* If no holder is provided via config, the editor will be appended to the element with this id
Expand Down Expand Up @@ -41,6 +42,21 @@ export default class Core {
*/
#caretAdapter: CaretAdapter;

/**
* Inline tool adapter is responsible for handling model formatting updates
* Applies format, got from inline toolbar to the model
* When model changed with formatting event, it renders related fragment
*/
#inlineToolsAdapter: InlineToolsAdapter;

/**
* @todo inline toolbar should subscripe on selection change event called by EventBus
* Inline toolbar is responsible for handling selection changes
* When model selection changes, it determines, whenever to show toolbar element,
* Which calls apply format method of the adapter
*/
#inlineToolbar: InlineToolbar;

/**
* @param config - Editor configuration
*/
Expand All @@ -55,6 +71,9 @@ export default class Core {

this.#toolsManager = new ToolsManager(this.#config.tools);
this.#caretAdapter = new CaretAdapter(this.#config.holder, this.#model);
this.#inlineToolsAdapter = new InlineToolsAdapter(this.#model, this.#caretAdapter);

this.#inlineToolbar = new InlineToolbar(this.#model, this.#inlineToolsAdapter, this.#toolsManager.getInlineTools(), config.holder!);

this.#model.initializeDocument({ blocks });
}
Expand Down Expand Up @@ -156,3 +175,5 @@ export default class Core {
return block;
}
}

export * from './entities/index.js';
20 changes: 18 additions & 2 deletions packages/core/src/tools/ToolsManager.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import type { InlineToolsConfig } from '@editorjs/sdk';
import type { BlockToolConstructor } from '../entities/BlockTool.js';
import { Paragraph } from './internal/paragraph/index.js';
import { Paragraph } from './internal/block-tools/paragraph/index.js';
import type { EditorConfig } from '@editorjs/editorjs';
import BoldInlineTool from './internal/inline-tools/bold/index.js';
import ItalicInlineTool from './internal/inline-tools/italic/index.js';

/**
* Works with tools
*/
export default class ToolsManager {
#tools: EditorConfig['tools'];

/**
* @param tools - Tools configuration passed by user
*/
constructor(private tools: EditorConfig['tools']) {
constructor(tools: EditorConfig['tools']) {
this.#tools = tools;
}

/**
Expand All @@ -24,4 +30,14 @@ export default class ToolsManager {
throw new Error(`Unknown tool: ${toolName}`);
}
}

/**
* Returns inline tools got from the EditorConfig tools
*/
public getInlineTools(): InlineToolsConfig {
return {
bold: BoldInlineTool,
italic: ItalicInlineTool,
};
};
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { BlockToolAdapter } from '@editorjs/dom-adapters';
import type { BlockTool, BlockToolConstructorOptions } from '../../../entities/BlockTool.js';
import type { BlockTool, BlockToolConstructorOptions } from '../../../../entities/BlockTool.js';

/**
* Base text block tool
Expand Down
72 changes: 72 additions & 0 deletions packages/core/src/tools/internal/inline-tools/bold/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { FormattingActionWithRange, InlineTool } from '@editorjs/sdk';
import type { InlineFragment, TextRange } from '@editorjs/model';
import { FormattingAction } from '@editorjs/model';
import { IntersectType } from '@editorjs/model';
import { make } from '@editorjs/dom';

/**
* Bold Tool
*
* Inline Toolbar Tool
*
* Makes selected text bolder
*/
export default class BoldInlineTool implements InlineTool {
/**
* Specifies Tool as Inline Toolbar Tool
* @returns {boolean}
*/
public static isInline = true;

/**
* Type of behaviour of the tool if new selection range intersect with existing fragment
* If two fragment intersect, they should be merged
*/
public intersectType: IntersectType = IntersectType.Extend;

/**
* Renders wrapper for tool without actual content
* @returns Created html element
*/
public createWrapper(): HTMLElement {
return make('b');
}

/**
* Returns formatting action and range for it to be applied
* @param index - index of current text selection
* @param fragments - all fragments of the bold inline tool inside of the current input
*/
public getAction(index: TextRange, fragments: InlineFragment[]): FormattingActionWithRange {
return {
action: this.isActive(index, fragments) ? FormattingAction.Unformat : FormattingAction.Format,
range: index,
};
};

/**
* Returns state of the bold inline tool
* @param index - index of current selection
* @param fragments - all fragments of the bold inline tool inside of the current input
* @returns true if tool is active, false otherwise
*/
public isActive(index: TextRange, fragments: InlineFragment[]): boolean {
let isActive = false;

fragments.forEach((fragment) => {
/**
* Check if current index is inside of model fragment
*/
if (index[0] >= fragment.range[0] && index[1] <= fragment.range[1]) {
isActive = true;

/**
* No need to check other fragments if state already chaned
*/
return;
}
});

return isActive;
}
}
72 changes: 72 additions & 0 deletions packages/core/src/tools/internal/inline-tools/italic/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { FormattingActionWithRange, InlineTool } from '@editorjs/sdk';
import type { InlineFragment, TextRange } from '@editorjs/model';
import { FormattingAction } from '@editorjs/model';
import { IntersectType } from '@editorjs/model';
import { make } from '@editorjs/dom';

/**
* Italic Tool
*
* Inline Toolbar Tool
*
* Makes selected text italic
*/
export default class ItalicInlineTool implements InlineTool {
/**
* Specifies Tool as Inline Toolbar Tool
* @returns {boolean}
*/
public static isInline = true;

/**
* Type of behaviour of the tool if new selection range intersect with existing fragment
* If two fragment intersect, they should be merged
*/
public intersectType: IntersectType = IntersectType.Extend;

/**
* Renders wrapper for tool without actual content
* @returns Created html element
*/
public createWrapper(): HTMLElement {
return make('i');
}

/**
* Returns formatting action and range for it to be applied
* @param index - index of current text selection
* @param fragments - all fragments of the bold inline tool inside of the current input
*/
public getAction(index: TextRange, fragments: InlineFragment[]): FormattingActionWithRange {
return {
action: this.isActive(index, fragments) ? FormattingAction.Unformat : FormattingAction.Format,
range: index,
};
};

/**
* Returns state of the bold inline tool
* @param index - index of current selection
* @param fragments - all fragments of the bold inline tool inside of the current input
* @returns true if tool is active, false otherwise
*/
public isActive(index: TextRange, fragments: InlineFragment[]): boolean {
let isActive = false;

fragments.forEach((fragment) => {
/**
* Check if current index is inside of model fragment
*/
if (index[0] >= fragment.range[0] && index[1] <= fragment.range[1]) {
isActive = true;

/**
* Don't need to check other fragments if state already chaned
*/
return;
}
});

return isActive;
}
}
Loading

1 comment on commit 50f0db4

@github-actions
Copy link

Choose a reason for hiding this comment

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

Coverage report for ./packages/model

St.
Category Percentage Covered / Total
🟢 Statements 100% 753/753
🟢 Branches 99.5% 201/202
🟢 Functions 98.37% 181/184
🟢 Lines 100% 725/725

Test suite run success

389 tests passing in 24 suites.

Report generated by 🧪jest coverage report action from 50f0db4

Please sign in to comment.