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(dom-adapters): Inline tool adapter check for tool required data #87

Merged
merged 77 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 76 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
8994e03
Implmenet global CaretAdapter
gohabereg Jan 24, 2024
6fa8279
Handle native inputs
gohabereg Jan 28, 2024
4f0de03
Merge branch 'main' of github.com:editor-js/document-model into featu…
gohabereg Feb 13, 2024
3f93468
Pass input type to Input component props
gohabereg Jun 3, 2024
7cde75e
Use class to represent index
gohabereg Aug 26, 2024
0679745
Fix lint in dom-adapters
gohabereg Aug 26, 2024
1f89c57
fix linter
gohabereg Aug 26, 2024
a7ecfe3
Merge branch 'main' of github.com:editor-js/document-model into featu…
gohabereg Aug 26, 2024
a6a8aa6
added inline tool adapter
e11sy Aug 27, 2024
713b8b9
Merge remote-tracking branch 'origin/feature/global-caret-adapter' in…
e11sy Aug 27, 2024
a9db501
implement model updates
e11sy Aug 27, 2024
06bacaa
update from main
e11sy Aug 28, 2024
d858afc
lint fix
e11sy Aug 28, 2024
83701e7
fix index
e11sy Aug 28, 2024
96bf644
adapter renders inline tools
e11sy Aug 28, 2024
d364498
lint fix and clean up
e11sy Aug 28, 2024
a4e26c9
jsdoc
e11sy Aug 28, 2024
bc01d1b
clean up
e11sy Aug 28, 2024
240affe
jsdoc
e11sy Aug 28, 2024
6853d2a
jsdoc
e11sy Aug 28, 2024
a68e8ea
Merge branch 'main' into inline-tool-adapter
e11sy Aug 28, 2024
5d8bbd1
surround content replaced
e11sy Aug 28, 2024
5c49a09
suggestions
e11sy Aug 28, 2024
01df103
lint fix
e11sy Aug 28, 2024
dea8caf
jsdoc
e11sy Aug 28, 2024
cc00ff3
Merge branch 'main' into inline-tool-adapter
e11sy Aug 28, 2024
9057d1a
added bold and italic inline tools into core package
e11sy Aug 28, 2024
a84d732
naming
e11sy Aug 28, 2024
da5ca7c
naming
e11sy Aug 28, 2024
cf5b177
added inline toolbar and inlineToolAdapter init into core
e11sy Aug 28, 2024
6998adc
update packages and lock
e11sy Aug 28, 2024
e6c4f7c
Merge branch 'main' into inline-tool-adapter
e11sy Aug 28, 2024
185d3f4
build fix
e11sy Aug 29, 2024
6e5034c
implement inline tool adapter to core
e11sy Aug 29, 2024
a629a5b
clean up
e11sy Aug 29, 2024
c0c9b84
Merge branch 'main' into inline-tool-adapter
e11sy Aug 29, 2024
e7c0e80
jsdoc and naming improvements
e11sy Aug 29, 2024
59d8840
naming
e11sy Aug 29, 2024
a2d5e30
naming
e11sy Aug 29, 2024
1adf8c5
renaming
e11sy Aug 29, 2024
e39d5bc
fix hardcoded
e11sy Aug 29, 2024
7f66686
tools are initialized inside of the inline toolbar initialization
e11sy Aug 29, 2024
d52475c
fixed inline tool attaching
e11sy Aug 29, 2024
ff16243
jsdoc
e11sy Aug 29, 2024
e9626bb
naming fix
e11sy Aug 29, 2024
eb4411f
fixed imports
e11sy Aug 29, 2024
4ccc47b
lint fix
e11sy Aug 29, 2024
530b09d
try build fix
e11sy Aug 29, 2024
3c9238b
install dependencies
e11sy Aug 29, 2024
91a1fef
add sdk package
e11sy Aug 29, 2024
cee8b33
fix build for core
e11sy Aug 29, 2024
f9725a1
change package name in actions
e11sy Aug 29, 2024
98a5644
add references
e11sy Aug 29, 2024
be50940
typo
e11sy Aug 29, 2024
6db4d90
fix build
e11sy Aug 29, 2024
6337cfa
added inline tool data former
e11sy Aug 29, 2024
4760931
fix lint
e11sy Aug 29, 2024
77ac4d7
update from main
e11sy Aug 29, 2024
758a317
rm unwanted changes
e11sy Aug 30, 2024
5345c8b
lint fix
e11sy Aug 30, 2024
8452550
fixed build
e11sy Aug 30, 2024
126bb5d
docs improved
e11sy Aug 30, 2024
4a24e82
Merge branch 'main' into inline-tool-adapter
e11sy Aug 30, 2024
cbef761
fix build
e11sy Aug 30, 2024
904ba7b
naming improved
e11sy Aug 30, 2024
6e3b42b
Update packages/core/src/ui/InlineToolbar/index.ts
e11sy Aug 30, 2024
70d078c
Update packages/dom-adapters/src/FormattingAdapter/index.ts
e11sy Aug 30, 2024
58335b4
rm unwanted change
e11sy Aug 30, 2024
f0e1335
Merge branch 'inline-tool-adapter' of https://github.com/editor-js/do…
e11sy Aug 30, 2024
e5fe984
naming
e11sy Aug 30, 2024
f03b984
separated renderToolActions and apply method in formatting adapter
e11sy Aug 30, 2024
0f23dda
naming
e11sy Aug 30, 2024
0e904a8
moved surround to utils
e11sy Aug 30, 2024
4efb7c4
lint fix
e11sy Aug 30, 2024
24307b0
Merge branch 'main' into inline-tool-adapter
e11sy Aug 30, 2024
3a3d488
last naming fix 🤞
e11sy Aug 30, 2024
e610765
made renderToolActions method private
e11sy Aug 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 packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ export default class Core {
this.#iocContainer = Container.of(Math.floor(Math.random() * 1e10).toString());

this.validateConfig(config);

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unnecessary-type-assertion
this.#config = config as CoreConfigValidated;

this.#iocContainer.set('EditorConfig', this.#config);
Expand Down
16 changes: 12 additions & 4 deletions packages/core/src/tools/ToolsManager.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { BlockToolConstructor } from '@editorjs/sdk';
import 'reflect-metadata';
import { deepMerge, isFunction, isObject, PromiseQueue } from '@editorjs/helpers';
import { Inject, Service } from 'typedi';
import type { BlockToolFacade, BlockTuneFacade,
InlineToolFacade } from './facades/index.js';
import {
BlockToolFacade, BlockTuneFacade,
InlineToolFacade,
ToolsCollection,
ToolsFactory
} from './facades/index.js';
Expand All @@ -13,10 +14,11 @@ import type {
ToolConstructable,
ToolSettings
} from '@editorjs/editorjs';
import { InlineTool, InlineToolConstructor } from '@editorjs/sdk';
import type { UnifiedToolConfig } from '../entities/index.js';
import BoldInlineTool from './internal/inline-tools/bold/index.js';
import ItalicInlineTool from './internal/inline-tools/italic/index.js';
import { BlockToolConstructor, InlineTool, InlineToolConstructor } from '@editorjs/sdk';
import { UnifiedToolConfig } from '../entities/index.js';
import LinkInlineTool from './internal/inline-tools/link/index.js';

/**
* Works with tools
Expand All @@ -25,6 +27,8 @@ import { UnifiedToolConfig } from '../entities/index.js';
*/
@Service()
export default class ToolsManager {
#tools: EditorConfig['tools'];

/**
* ToolsFactory instance
*/
Expand Down Expand Up @@ -234,6 +238,10 @@ export default class ToolsManager {
class: ItalicInlineTool as unknown as InlineToolConstructor,
isInternal: true,
},
link: {
class: LinkInlineTool as unknown as InlineToolConstructor,
gohabereg marked this conversation as resolved.
Show resolved Hide resolved
isInternal: true,
},
};
}
}
7 changes: 7 additions & 0 deletions packages/core/src/tools/facades/InlineToolFacade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ export class InlineToolFacade extends BaseToolFacade<ToolType.Inline, IInlineToo
return this.constructable[InternalInlineToolSettings.Title];
}

/**
* Checks if actions element could be rendered by tool
*/
public get hasActions(): boolean {
return 'renderActions' in this.constructable.prototype;
}

/**
* Constructs new InlineTool instance from constructable
*/
Expand Down
16 changes: 8 additions & 8 deletions packages/core/src/tools/internal/inline-tools/bold/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { FormattingActionWithRange, InlineTool } from '@editorjs/sdk';
import type { ToolFormattingOptions, InlineTool } from '@editorjs/sdk';
import type { InlineFragment, TextRange } from '@editorjs/model';
import { FormattingAction } from '@editorjs/model';
import { IntersectType } from '@editorjs/model';
Expand Down Expand Up @@ -34,30 +34,30 @@ export default class BoldInlineTool implements InlineTool {

/**
* Returns formatting action and range for it to be applied
* @param index - index of current text selection
* @param range - range 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 {
public getFormattingOptions(range: TextRange, fragments: InlineFragment[]): ToolFormattingOptions {
return {
action: this.isActive(index, fragments) ? FormattingAction.Unformat : FormattingAction.Format,
range: index,
action: this.isActive(range, fragments) ? FormattingAction.Unformat : FormattingAction.Format,
range,
};
};

/**
* Returns state of the bold inline tool
* @param index - index of current selection
* @param range - range 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 {
public isActive(range: 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]) {
if (range[0] >= fragment.range[0] && range[1] <= fragment.range[1]) {
isActive = true;

/**
Expand Down
16 changes: 8 additions & 8 deletions packages/core/src/tools/internal/inline-tools/italic/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { FormattingActionWithRange, InlineTool } from '@editorjs/sdk';
import type { ToolFormattingOptions, InlineTool } from '@editorjs/sdk';
import type { InlineFragment, TextRange } from '@editorjs/model';
import { FormattingAction } from '@editorjs/model';
import { IntersectType } from '@editorjs/model';
Expand Down Expand Up @@ -34,30 +34,30 @@ export default class ItalicInlineTool implements InlineTool {

/**
* Returns formatting action and range for it to be applied
* @param index - index of current text selection
* @param range - range 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 {
public getFormattingOptions(range: TextRange, fragments: InlineFragment[]): ToolFormattingOptions {
return {
action: this.isActive(index, fragments) ? FormattingAction.Unformat : FormattingAction.Format,
range: index,
action: this.isActive(range, fragments) ? FormattingAction.Unformat : FormattingAction.Format,
range,
};
};

/**
* Returns state of the bold inline tool
* @param index - index of current selection
* @param range - range 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 {
public isActive(range: 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]) {
if (range[0] >= fragment.range[0] && range[1] <= fragment.range[1]) {
isActive = true;

/**
Expand Down
101 changes: 101 additions & 0 deletions packages/core/src/tools/internal/inline-tools/link/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import type { ActionsElementWithOptions, ToolFormattingOptions, InlineTool, InlineToolFormatData } 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';

/**
* Link Tool
*
* Inline Toolbar Tool
*
* Makes selected text linked
*/
export default class LinkInlineTool 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, existing fragment should be replaced with new one
*/
public intersectType: IntersectType = IntersectType.Replace;

/**
* Renders wrapper for tool without actual content
* @param data - inline tool data formed in toolbar
* @returns Created html element
*/
public createWrapper(data: InlineToolFormatData): HTMLElement {
const linkElement = make('a') as HTMLLinkElement;

if (typeof data.link === 'string') {
linkElement.href = data.link;
}

return linkElement;
}

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

/**
* Returns state of the bold inline tool
* @param range - range 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(range: TextRange, fragments: InlineFragment[]): boolean {
let isActive = false;

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

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

return isActive;
}

/**
* Function that is responsible for rendering data form element
* @param callback function that should be triggered, when data completely formed
* @returns rendered data form element with options required in toolbar
*/
public renderActions(callback: (data: InlineToolFormatData) => void): ActionsElementWithOptions {
const linkInput = make('input') as HTMLInputElement;

linkInput.addEventListener('keydown', (event: KeyboardEvent) => {
if (event.key === 'Enter') {
/**
* Remove link input, when data formed and trigger callback
*/
linkInput.remove();

callback({ link: linkInput.value });
}
});

return { element: linkInput };
}
}
65 changes: 55 additions & 10 deletions packages/core/src/ui/InlineToolbar/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { FormattingAdapter } from '@editorjs/dom-adapters';
import type { InlineToolFormatData } from '@editorjs/sdk';
import type { InlineToolName } from '@editorjs/model';
import { type EditorJSModel, type TextRange, createInlineToolData, Index } from '@editorjs/model';
import { type EditorJSModel, type TextRange, createInlineToolData, createInlineToolName, Index } from '@editorjs/model';
import { EventType } from '@editorjs/model';
import { make } from '@editorjs/dom';
import type { InlineToolFacade, ToolsCollection } from '../../tools/facades/index.js';
Expand Down Expand Up @@ -38,6 +39,14 @@ export class InlineToolbar {
*/
#toolbar: HTMLElement | undefined = undefined;

/**
* Actions of the current tool html element rendered inside of the toolbar element
*/
#actionsElement: HTMLElement | undefined = undefined;

/**
* Holder element of the editor
*/
#holder: HTMLElement;

/**
Expand Down Expand Up @@ -123,14 +132,25 @@ export class InlineToolbar {

this.#toolbar = make('div');

Array.from(this.#tools.keys()).forEach((toolName) => {
this.#tools.forEach((tool, toolName) => {
const inlineElementButton = make('button');

inlineElementButton.innerHTML = toolName;

inlineElementButton.addEventListener('click', (_event) => {
this.apply(toolName as InlineToolName);
});
/**
* If tool has actions, then on click of the element button we should render actions element
* If tool has no action, then on click of the element button we should apply format
*/
if (tool.hasActions) {
neSpecc marked this conversation as resolved.
Show resolved Hide resolved
inlineElementButton.addEventListener('click', (_event) => {
this.renderToolActions(createInlineToolName(toolName));
});
} else {
inlineElementButton.addEventListener('click', (_event) => {
this.apply(createInlineToolName(toolName), createInlineToolData({}));
});
}

if (this.#toolbar !== undefined) {
this.#toolbar.appendChild(inlineElementButton);
}
Expand All @@ -147,13 +167,38 @@ export class InlineToolbar {
}

/**
* Apply format with data formed in toolbar
* @param toolName - name of the inline tool, whose format would be applied
* Render actions to form data, which is required in tool
* Element that is used for forming data is rendered inside of the tool instance
* This function adds actions element to the toolbar
* @param nameOfTheTool - name of the inline tool, whose format would be applied
*/
public apply(toolName: InlineToolName): void {
public renderToolActions(nameOfTheTool: InlineToolName): void {
Copy link
Member

Choose a reason for hiding this comment

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

Should it be public?

const elementWithOptions = this.#formattingAdapter.createToolActions(nameOfTheTool, (data: InlineToolFormatData): void => {
this.apply(nameOfTheTool, data);
});

if (this.#toolbar === undefined) {
throw new Error('InlineToolbar: can not show tool actions without toolbar');
}

/**
* @todo pass to applyFormat inline tool data formed in toolbar
* If actions element already exists, replace it with new one
* This check is needed to prevent displaying of several actions elements
*/
this.#formattingAdapter.applyFormat(toolName, createInlineToolData({}));
if (this.#actionsElement !== undefined) {
this.#actionsElement.remove();
}

this.#actionsElement = elementWithOptions.element;
this.#holder.appendChild(this.#actionsElement);
};

/**
* Apply format of the inline tool to the model
* @param toolName - name of the tool which format would be applied
* @param formatData - formed data required in the inline tool
*/
public apply(toolName: InlineToolName, formatData: InlineToolFormatData): void {
neSpecc marked this conversation as resolved.
Show resolved Hide resolved
this.#formattingAdapter.applyFormat(toolName, createInlineToolData(formatData));
}
}
2 changes: 1 addition & 1 deletion packages/dom-adapters/src/BlockToolAdapter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from '../utils/index.js';
import { InputType } from './types/InputType.js';
import type { BlockToolAdapter as BlockToolAdapterInterface } from '@editorjs/sdk';
import type { FormattingAdapter } from '../InlineToolsAdapter/index.js';
import type { FormattingAdapter } from '../FormattingAdapter/index.js';

/**
* BlockToolAdapter is using inside Block tools to connect browser DOM elements to the model
Expand Down
Loading
Loading