Skip to content

Commit

Permalink
Fix InlineParentNode.insertAfter method & add isActive state for inli…
Browse files Browse the repository at this point in the history
…ne toolbar buttons
  • Loading branch information
gohabereg committed Sep 2, 2024
1 parent 83eafef commit 05107db
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { InlineTool } from '@editorjs/sdk';
import { CoreEventBase } from './CoreEventBase.js';
import { CoreEventType } from './CoreEventType.js';
import type { Index, InlineToolName } from '@editorjs/model';
import type { Index, InlineFragment, InlineToolName } from '@editorjs/model';

/**
* Payload of SelectionChangedCoreEvent custom event
Expand All @@ -17,6 +17,11 @@ export interface SelectionChangedCoreEventPayload {
* Inline tools available for the current selection
*/
readonly availableInlineTools: Map<InlineToolName, InlineTool>;

/**
* Inline fragments available for the current selection
*/
readonly fragments: InlineFragment[];
}

/**
Expand Down
17 changes: 14 additions & 3 deletions packages/core/src/components/SelectionManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'reflect-metadata';
import { FormattingAdapter } from '@editorjs/dom-adapters';
import type { CaretManagerEvents, InlineToolName } from '@editorjs/model';
import type { CaretManagerEvents, InlineFragment, InlineToolName } from '@editorjs/model';
import { CaretManagerCaretUpdatedEvent, Index, EditorJSModel, createInlineToolData, createInlineToolName } from '@editorjs/model';
import { EventType } from '@editorjs/model';
import { Service } from 'typedi';
Expand Down Expand Up @@ -73,15 +73,26 @@ export class SelectionManager {
*/
#handleCaretManagerUpdate(event: CaretManagerEvents): void {
switch (true) {
case event instanceof CaretManagerCaretUpdatedEvent:
case event instanceof CaretManagerCaretUpdatedEvent: {
const { index: serializedIndex } = event.detail;
const index = serializedIndex !== null ? Index.parse(serializedIndex) : null;
let fragments: InlineFragment[] = [];

if (index !== null && index.blockIndex !== undefined && index.dataKey !== undefined && index.textRange !== undefined) {
fragments = this.#model.getFragments(index.blockIndex, index.dataKey, ...index.textRange);
}

this.#eventBus.dispatchEvent(new SelectionChangedCoreEvent({
index: event.detail.index !== null ? Index.parse(event.detail.index) : null,
index,
/**
* @todo implement filter by current BlockTool configuration
*/
availableInlineTools: this.#inlineTools,
fragments,
}));

break;
}
default:
break;
}
Expand Down
16 changes: 12 additions & 4 deletions packages/core/src/ui/InlineToolbar/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { InlineToolbarRenderedUIEvent } from './InlineToolbarRenderedUIEvent.js'
import { CoreEventType, EventBus, SelectionChangedCoreEvent } from '../../components/EventBus/index.js';
import { EditorAPI } from '../../api/index.js';
import { InlineTool, InlineToolFormatData } from '@editorjs/sdk';
import { InlineToolName } from '@editorjs/model';
import { InlineFragment, InlineToolName, TextRange } from '@editorjs/model';
import { CoreConfigValidated } from '../../entities/index.js';

/**
Expand Down Expand Up @@ -56,7 +56,7 @@ export class InlineToolbarUI {
* @param event - SelectionChangedCoreEvent event
*/
#handleSelectionChange(event: SelectionChangedCoreEvent): void {
const { availableInlineTools, index } = event.detail;
const { availableInlineTools, index, fragments } = event.detail;
const selection = window.getSelection();

if (
Expand All @@ -75,7 +75,7 @@ export class InlineToolbarUI {
return;
}

this.#updateToolsList(availableInlineTools);
this.#updateToolsList(availableInlineTools, index.textRange, fragments);
this.#move();
this.#show();
}
Expand Down Expand Up @@ -137,15 +137,23 @@ export class InlineToolbarUI {
/**
* Renders the list of available inline tools in the Inline Toolbar
* @param tools - Inline Tools available for the current selection
* @param textRange - current selection text range
* @param fragments - inline fragments for the current selection
*/
#updateToolsList(tools: Map<InlineToolName, InlineTool>): void {
#updateToolsList(tools: Map<InlineToolName, InlineTool>, textRange: TextRange, fragments: InlineFragment[]): void {
this.#nodes.buttons.innerHTML = '';

Array.from(tools.entries()).forEach(([name, tool]) => {
const button = make('button');

button.textContent = name;

const isActive = tool.isActive(textRange, fragments.filter((fragment: InlineFragment) => fragment.tool === name));

if (isActive) {
button.style.fontWeight = 'bold';
}

if (Object.hasOwnProperty.call(tool.constructor.prototype, 'renderActions')) {
button.addEventListener('click', () => {
this.#renderToolActions(name, tool);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ export function ParentNode<C extends { new(...args: any[]): InlineNode }>(constr
* @param children - children nodes to insert
*/
public insertAfter(target: ChildNode, ...children: ChildNode[]): void {

Check warning on line 139 in packages/model/src/entities/inline-fragments/mixins/ParentNode/index.ts

View workflow job for this annotation

GitHub Actions / lint

Block must not be padded by blank lines

/**
* We need to get the index first before any manipulations with children array
*/
const index = this.children.indexOf(target);

/**
* Append children to the parent to set their parent property
*/
Expand All @@ -154,8 +160,6 @@ export function ParentNode<C extends { new(...args: any[]): InlineNode }>(constr
/**
* Insert added children to correct places
*/
const index = this.children.indexOf(target);

this.children.splice(index + 1, 0, ...children);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,19 @@ describe('ParentNode mixin', () => {

expect(dummy.children).toEqual([childMock, anotherChildMock, childMockToInsert]);
});

it('should correctly insert children when it contains the target child', () => {
const childMock = new ChildDummy();
const anotherChildMock = new ChildDummy();
const childMockToInsert = new ChildDummy();
const anotherChildMockToInsert = new ChildDummy();

dummy.append(childMock, childMockToInsert, anotherChildMock);

dummy.insertAfter(childMockToInsert, anotherChildMockToInsert, childMockToInsert);

expect(dummy.children).toEqual([childMock, anotherChildMock, anotherChildMockToInsert, childMockToInsert]);
});
});

describe('.removeChild()', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/playground/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ onMounted(() => {
} ],
},
onModelUpdate: (model: EditorJSModel) => {
window.model = model;
serialized.value = model.serialized;
editorDocument.value = model.devModeGetDocument();
},
Expand Down Expand Up @@ -132,6 +133,8 @@ onMounted(() => {
background-color: #111;
border-radius: 8px;
padding: 10px;
font-size: 2em;
}
.sectionHeading {
Expand Down

0 comments on commit 05107db

Please sign in to comment.