Skip to content

Commit

Permalink
Merge pull request #4138 from udecode/fix/skip-mark
Browse files Browse the repository at this point in the history
Fix mention plugin conflict with skip mark plugin
  • Loading branch information
felixfeng33 authored Mar 4, 2025
2 parents 8379af6 + 575d949 commit 1016b77
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/gold-gorillas-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@udecode/plate-basic-marks': patch
---

Fix mention plugin conflict with skip mark plugin
168 changes: 168 additions & 0 deletions packages/basic-marks/src/lib/BaseSkipMarkPlugin.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/** @jsx jsxt */

import type { SlateEditor } from '@udecode/plate';

import { BaseParagraphPlugin, createSlateEditor } from '@udecode/plate';
import { jsxt } from '@udecode/plate-test-utils';

import { BaseBoldPlugin } from './BaseBoldPlugin';
import { BaseCodePlugin } from './BaseCodePlugin';
import { BaseItalicPlugin } from './BaseItalicPlugin';
import { BaseSkipMarkPlugin } from './BaseSkipMarkPlugin';

jsxt;

const skipMarkPlugin = BaseSkipMarkPlugin.configure({
options: {
query: {
allow: [BaseCodePlugin.key],
},
},
});
const plugins = [
BaseParagraphPlugin,
BaseBoldPlugin,
BaseCodePlugin,
BaseItalicPlugin,
skipMarkPlugin,
];

describe('BaseSkipMarkPlugin', () => {
describe('insertText', () => {
describe('when cursor is not in a mark', () => {
it('should insert text normally', () => {
const input = (
<editor>
<hp>
test
<cursor />
</hp>
</editor>
) as any as SlateEditor;

const output = (
<editor>
<hp>
testinserted
<cursor />
</hp>
</editor>
) as any as SlateEditor;

const editor = createSlateEditor({
plugins,
selection: input.selection,
value: input.children,
});

editor.tf.insertText('inserted');

expect(editor.children).toEqual(output.children);
});
});

describe('when cursor is at the end of a mark', () => {
it('should remove marks when inserting text', () => {
const input = (
<editor>
<hp>
<htext code>test</htext>
<cursor />
</hp>
</editor>
) as any as SlateEditor;

const output = (
<editor>
<hp>
<htext code>test</htext>
<htext>inserted</htext>
<cursor />
</hp>
</editor>
) as any as SlateEditor;

const editor = createSlateEditor({
plugins,
selection: input.selection,
value: input.children,
});

editor.tf.insertText('inserted');

expect(editor.children).toEqual(output.children);
});

it('should not remove marks when between same marks', () => {
const input = (
<editor>
<hp>
<htext code>test</htext>
<cursor />
<htext bold code>
more
</htext>
</hp>
</editor>
) as any as SlateEditor;

const output = (
<editor>
<hp>
<htext code>testinserted</htext>
<cursor />
<htext bold code>
more
</htext>
</hp>
</editor>
) as any as SlateEditor;

const editor = createSlateEditor({
plugins,
selection: input.selection,
value: input.children,
});

editor.tf.insertText('inserted');

expect(editor.children).toEqual(output.children);
});
});

describe('when different mark types are adjacent', () => {
it('should remove marks when inserting text', () => {
const input = (
<editor>
<hp>
<htext code>test</htext>
<cursor />
<htext italic>more</htext>
</hp>
</editor>
) as any as SlateEditor;

const output = (
<editor>
<hp>
<htext code>test</htext>
<htext>inserted</htext>
<cursor />
<htext italic>more</htext>
</hp>
</editor>
) as any as SlateEditor;

const editor = createSlateEditor({
plugins,
selection: input.selection,
value: input.children,
});

editor.tf.insertText('inserted');

expect(editor.children).toEqual(output.children);
});
});
});
});
60 changes: 39 additions & 21 deletions packages/basic-marks/src/lib/BaseSkipMarkPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,71 @@
import {
type PluginConfig,
type QueryNodeOptions,
type Text,
createTSlatePlugin,
queryNode,
RangeApi,
TextApi,
} from '@udecode/plate';

export type SkipMarkConfig = PluginConfig<
'skip-mark',
{
query?: QueryNodeOptions;
query: {
allow: string[];
};
}
>;

export const BaseSkipMarkPlugin = createTSlatePlugin<SkipMarkConfig>({
key: 'skip-mark',
options: {},
options: {
query: {
allow: [],
},
},
}).overrideEditor(({ editor, getOption, tf: { insertText } }) => ({
transforms: {
insertText(text, options) {
if (RangeApi.isExpanded(editor.selection))
return insertText(text, options);

const allow = getOption('query').allow;

const textNode = editor.api.node<Text>({
mode: 'lowest',
match: (node) => {
if (TextApi.isText(node)) {
return allow.some((key) => !!node[key]);
}
},
});

const query = getOption('query');
if (!textNode) return insertText(text, options);

if (
textNode &&
!queryNode(textNode, query) &&
editor.api.isEnd(editor.selection?.focus, textNode[1])
) {
editor.tf.insertNode({ text });
const _nextPoint = editor.api.start(textNode[1], { next: true });
const nextPoint = editor.api.start(textNode[1], { next: true });

if (!_nextPoint) return;
const nextNode =
nextPoint &&
editor.api.node<Text>({
at: nextPoint,
mode: 'lowest',
match: (node) => {
if (TextApi.isText(node)) {
return allow.some((key) => !!node[key]);
}
},
});

const nextPoint = {
offset: _nextPoint.offset + 1,
path: _nextPoint.path,
};
const isBetweenSameMarks =
nextNode &&
allow.findIndex((key) => !!textNode[0][key]) ===
allow.findIndex((key) => !!nextNode[0][key]);

editor.tf.setSelection({
anchor: nextPoint,
focus: nextPoint,
});
if (
!isBetweenSameMarks &&
editor.api.isEnd(editor.selection?.focus, textNode[1])
) {
editor.tf.removeMarks(allow);
insertText(text, options);

return;
}
Expand Down

0 comments on commit 1016b77

Please sign in to comment.