@atlaskit/prosemirror-input-rules 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +100 -28
- package/afm-jira/tsconfig.json +20 -0
- package/dist/types/types.d.ts +1 -1
- package/dist/types-ts4.5/types.d.ts +1 -1
- package/package.json +53 -55
- package/report.api.md +14 -17
- package/src/__tests__/unit/handler.ts +372 -374
- package/src/__tests__/unit/plugin.ts +154 -165
- package/src/editor-common.ts +13 -17
- package/src/handler.ts +121 -135
- package/src/index.ts +3 -3
- package/src/plugin.ts +106 -108
- package/src/types.ts +12 -23
- package/src/utils.ts +45 -55
- package/tsconfig.app.json +33 -33
- package/tsconfig.dev.json +43 -43
|
@@ -10,169 +10,158 @@ import { doc, p } from '@atlaskit/editor-test-helpers/doc-builder';
|
|
|
10
10
|
import { createInputRulePlugin } from '../../plugin';
|
|
11
11
|
|
|
12
12
|
describe('input-tule/plugin/createInputRulePlugin', () => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const nextEditorState = createEditorState(doc(p('here ```')));
|
|
168
|
-
|
|
169
|
-
const [argEditorState] = handlerMock.mock.calls[1];
|
|
170
|
-
|
|
171
|
-
expect(argEditorState.doc.toJSON()).toEqual(
|
|
172
|
-
nextEditorState.doc.toJSON(),
|
|
173
|
-
);
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
});
|
|
13
|
+
let editorViewElement: HTMLElement;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
document.body.innerHTML = '';
|
|
17
|
+
editorViewElement = document.createElement('div');
|
|
18
|
+
document.body.appendChild(editorViewElement);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function insertText(view: EditorView, text: string) {
|
|
22
|
+
const { $from, $to } = view.state.selection;
|
|
23
|
+
view.someProp('handleTextInput', (f) => f(view, $from.pos, $to.pos, text));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function createEditorView(lol: DocBuilder, customInputRule: SafePlugin) {
|
|
27
|
+
const editorState = createEditorState(lol, customInputRule);
|
|
28
|
+
|
|
29
|
+
return new EditorView(editorViewElement, {
|
|
30
|
+
state: editorState,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('inline code transformation: ', () => {
|
|
35
|
+
let handlerMock: jest.Mock;
|
|
36
|
+
let editorView: EditorView;
|
|
37
|
+
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
handlerMock = jest.fn().mockImplementation((state: EditorState) => {
|
|
40
|
+
return state.tr;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const INLINE_CODE_REGEX = /(\S*)(`[^\s][^`]*`)$/;
|
|
44
|
+
const myCustomInputRule = new SafePlugin(
|
|
45
|
+
createInputRulePlugin(
|
|
46
|
+
'lol',
|
|
47
|
+
[
|
|
48
|
+
{
|
|
49
|
+
match: INLINE_CODE_REGEX,
|
|
50
|
+
handler: handlerMock,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
{
|
|
54
|
+
allowInsertTextOnDocument: true,
|
|
55
|
+
},
|
|
56
|
+
),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
editorView = createEditorView(doc(p('here `some{<} words{>}')), myCustomInputRule);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('when the handler is called', () => {
|
|
63
|
+
describe('during the textInput flow', () => {
|
|
64
|
+
it('should call the handler only once', () => {
|
|
65
|
+
jest.spyOn(editorView, 'dispatch').mockImplementation((tr) => {});
|
|
66
|
+
insertText(editorView, '`');
|
|
67
|
+
|
|
68
|
+
expect(handlerMock).toHaveBeenCalledTimes(1);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should send the editor state without the inserted text', () => {
|
|
72
|
+
insertText(editorView, '`');
|
|
73
|
+
const nextEditorState = createEditorState(doc(p('here `some words')));
|
|
74
|
+
|
|
75
|
+
const [argEditorState] = handlerMock.mock.calls[0];
|
|
76
|
+
|
|
77
|
+
expect(argEditorState.doc.toJSON()).toEqual(nextEditorState.doc.toJSON());
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('after the appendTransaction flow', () => {
|
|
82
|
+
it('should call the handler only twice', () => {
|
|
83
|
+
insertText(editorView, '`');
|
|
84
|
+
|
|
85
|
+
expect(handlerMock).toHaveBeenCalledTimes(2);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should send the editor state with the inserted text', () => {
|
|
89
|
+
insertText(editorView, '`');
|
|
90
|
+
const nextEditorState = createEditorState(doc(p('here `some`')));
|
|
91
|
+
|
|
92
|
+
const [argEditorState] = handlerMock.mock.calls[1];
|
|
93
|
+
|
|
94
|
+
expect(argEditorState.doc.toJSON()).toEqual(nextEditorState.doc.toJSON());
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('code block node: ', () => {
|
|
101
|
+
let handlerMock: jest.Mock;
|
|
102
|
+
let editorView: EditorView;
|
|
103
|
+
|
|
104
|
+
beforeEach(() => {
|
|
105
|
+
handlerMock = jest.fn().mockImplementation((state: EditorState) => {
|
|
106
|
+
return state.tr;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const CODE_BLOCK_REGEX = /(?!\s)(`{3,})/;
|
|
110
|
+
const myCustomInputRule = new SafePlugin(
|
|
111
|
+
createInputRulePlugin(
|
|
112
|
+
'lol',
|
|
113
|
+
[
|
|
114
|
+
{
|
|
115
|
+
match: CODE_BLOCK_REGEX,
|
|
116
|
+
handler: handlerMock,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
{
|
|
120
|
+
allowInsertTextOnDocument: true,
|
|
121
|
+
},
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
editorView = createEditorView(doc(p('here {<>}')), myCustomInputRule);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('when the handler is called', () => {
|
|
129
|
+
describe('during the textInput flow', () => {
|
|
130
|
+
it('should call the handler only once', () => {
|
|
131
|
+
jest.spyOn(editorView, 'dispatch').mockImplementation((tr) => {});
|
|
132
|
+
insertText(editorView, '```');
|
|
133
|
+
|
|
134
|
+
expect(handlerMock).toHaveBeenCalledTimes(1);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should send the editor state without the inserted text', () => {
|
|
138
|
+
insertText(editorView, '```');
|
|
139
|
+
|
|
140
|
+
const nextEditorState = createEditorState(doc(p('here ')));
|
|
141
|
+
|
|
142
|
+
const [argEditorState] = handlerMock.mock.calls[0];
|
|
143
|
+
|
|
144
|
+
expect(argEditorState.doc.toJSON()).toEqual(nextEditorState.doc.toJSON());
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe('after the appendTransaction flow', () => {
|
|
149
|
+
it('should call the handler only twice', () => {
|
|
150
|
+
insertText(editorView, '```');
|
|
151
|
+
|
|
152
|
+
expect(handlerMock).toHaveBeenCalledTimes(2);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should send the editor state with the inserted text', () => {
|
|
156
|
+
insertText(editorView, '```');
|
|
157
|
+
|
|
158
|
+
const nextEditorState = createEditorState(doc(p('here ```')));
|
|
159
|
+
|
|
160
|
+
const [argEditorState] = handlerMock.mock.calls[1];
|
|
161
|
+
|
|
162
|
+
expect(argEditorState.doc.toJSON()).toEqual(nextEditorState.doc.toJSON());
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
});
|
|
178
167
|
});
|
package/src/editor-common.ts
CHANGED
|
@@ -4,35 +4,31 @@
|
|
|
4
4
|
*
|
|
5
5
|
* This simplifies our dependencies and makes adoption much easier.
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
8
|
-
EditorState,
|
|
9
|
-
Selection,
|
|
10
|
-
Transaction,
|
|
11
|
-
} from '@atlaskit/editor-prosemirror/state';
|
|
7
|
+
import type { EditorState, Selection, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
12
8
|
|
|
13
9
|
export type InputRuleHandler = (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
state: EditorState,
|
|
11
|
+
matchResult: RegExpExecArray,
|
|
12
|
+
start: number,
|
|
13
|
+
end: number,
|
|
18
14
|
) => Transaction | null;
|
|
19
15
|
|
|
20
16
|
export type OnHandlerApply = (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
state: EditorState,
|
|
18
|
+
tr: Transaction,
|
|
19
|
+
matchResult: RegExpExecArray,
|
|
24
20
|
) => void;
|
|
25
21
|
|
|
26
22
|
export interface InputRuleWrapper {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
match: RegExp;
|
|
24
|
+
handler: InputRuleHandler;
|
|
25
|
+
onHandlerApply?: OnHandlerApply;
|
|
30
26
|
}
|
|
31
27
|
|
|
32
28
|
// This is to workaround requiring editor-common in this repository which
|
|
33
29
|
// makes the dependency tree significantly more complex since editor-common
|
|
34
30
|
// needs to be a singleton
|
|
35
31
|
export const isGapCursorSelection = (selection: Selection): boolean => {
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
// @ts-expect-error
|
|
33
|
+
return Boolean(selection.jsonID === 'gapcursor');
|
|
38
34
|
};
|
package/src/handler.ts
CHANGED
|
@@ -1,154 +1,140 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
EditorState,
|
|
3
|
-
PluginKey,
|
|
4
|
-
Transaction,
|
|
5
|
-
} from '@atlaskit/editor-prosemirror/state';
|
|
1
|
+
import type { EditorState, PluginKey, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
6
2
|
|
|
7
3
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
leafNodeReplacementCharacter,
|
|
5
|
+
MAX_REGEX_MATCH,
|
|
6
|
+
TEXT_INPUT_RULE_TRANSACTION_KEY,
|
|
11
7
|
} from './constants';
|
|
12
8
|
import { isGapCursorSelection } from './editor-common';
|
|
13
9
|
import type {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
HandleInputEvent,
|
|
11
|
+
InputRulePluginState,
|
|
12
|
+
InputRuleWrapper,
|
|
13
|
+
MatchedRule,
|
|
14
|
+
OnBeforeRegexMatch,
|
|
15
|
+
OnInputEvent,
|
|
20
16
|
} from './types';
|
|
21
17
|
|
|
22
18
|
type Options = {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
pluginKey: PluginKey;
|
|
20
|
+
rules: InputRuleWrapper[];
|
|
21
|
+
allowInsertTextOnDocument: boolean;
|
|
22
|
+
onInputEvent?: OnInputEvent;
|
|
23
|
+
onBeforeRegexMatch?: OnBeforeRegexMatch;
|
|
28
24
|
};
|
|
29
25
|
|
|
30
26
|
export const createInputEventHandler =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return true;
|
|
90
|
-
};
|
|
27
|
+
({
|
|
28
|
+
rules,
|
|
29
|
+
pluginKey,
|
|
30
|
+
allowInsertTextOnDocument,
|
|
31
|
+
onInputEvent,
|
|
32
|
+
onBeforeRegexMatch,
|
|
33
|
+
}: Options): HandleInputEvent =>
|
|
34
|
+
({ view, from, to, text }) => {
|
|
35
|
+
if (view.composing) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const state = view.state;
|
|
40
|
+
const $from = state.doc.resolve(from);
|
|
41
|
+
|
|
42
|
+
if ($from.parent.type.spec.code) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
if (onInputEvent && !onInputEvent({ state, from, to })) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const textBefore =
|
|
50
|
+
$from.parent.textBetween(
|
|
51
|
+
Math.max(0, $from.parentOffset - MAX_REGEX_MATCH),
|
|
52
|
+
$from.parentOffset,
|
|
53
|
+
undefined,
|
|
54
|
+
leafNodeReplacementCharacter,
|
|
55
|
+
) + text;
|
|
56
|
+
|
|
57
|
+
const result = findMatchOnRules({
|
|
58
|
+
rules,
|
|
59
|
+
textBefore,
|
|
60
|
+
from,
|
|
61
|
+
to,
|
|
62
|
+
state,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (!result) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const tr = allowInsertTextOnDocument ? state.tr.insertText(text, from, to) : state.tr;
|
|
70
|
+
tr.setMeta(TEXT_INPUT_RULE_TRANSACTION_KEY, true);
|
|
71
|
+
tr.setMeta(pluginKey, {
|
|
72
|
+
textInserted: text,
|
|
73
|
+
from: result.from,
|
|
74
|
+
to: result.to,
|
|
75
|
+
matchedRule: result.matchedRule,
|
|
76
|
+
} as InputRulePluginState);
|
|
77
|
+
|
|
78
|
+
if (onBeforeRegexMatch) {
|
|
79
|
+
onBeforeRegexMatch(tr);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
view.dispatch(tr);
|
|
83
|
+
return true;
|
|
84
|
+
};
|
|
91
85
|
|
|
92
86
|
type FindMatchOnRulesProps = {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
87
|
+
rules: InputRuleWrapper[];
|
|
88
|
+
textBefore: string;
|
|
89
|
+
from: number;
|
|
90
|
+
to: number;
|
|
91
|
+
state: EditorState;
|
|
98
92
|
};
|
|
99
93
|
type RuleMatchedResult = {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
94
|
+
from: number;
|
|
95
|
+
to: number;
|
|
96
|
+
matchedRule: MatchedRule;
|
|
103
97
|
};
|
|
104
98
|
function findMatchOnRules({
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
99
|
+
rules,
|
|
100
|
+
textBefore,
|
|
101
|
+
from,
|
|
102
|
+
to,
|
|
103
|
+
state,
|
|
110
104
|
}: FindMatchOnRulesProps): RuleMatchedResult | null {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
...rule,
|
|
147
|
-
result: match,
|
|
148
|
-
},
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return null;
|
|
105
|
+
for (let i = 0; i < rules.length; i++) {
|
|
106
|
+
const rule = rules[i];
|
|
107
|
+
|
|
108
|
+
// Some plugins like Typeahead require a whitespace before a trigger character.
|
|
109
|
+
// We want them to fire inside a gap cursor. Yet, a gap cursor is not considered a whitespace,
|
|
110
|
+
// and `textBefore` contains the text in the previous block before the gap cursor.
|
|
111
|
+
// Here is a workaround: if we inside a gap cursor, match the input rule only against the last typed character
|
|
112
|
+
// (which may be a typeahead trigger) and ignore the rest.
|
|
113
|
+
const matchString: string = isGapCursorSelection(state.selection)
|
|
114
|
+
? textBefore.at(-1) ?? ''
|
|
115
|
+
: textBefore;
|
|
116
|
+
|
|
117
|
+
const match = rule.match.exec(matchString);
|
|
118
|
+
if (!match) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const parentNodeStartAt = state.selection.$from.start();
|
|
123
|
+
const offset = Math.max(0, state.selection.$from.parentOffset - MAX_REGEX_MATCH);
|
|
124
|
+
const fromFixed = Math.max(parentNodeStartAt + match.index + offset, 1);
|
|
125
|
+
const transform: Transaction | null = rule.handler(state, match, fromFixed, to);
|
|
126
|
+
|
|
127
|
+
if (transform) {
|
|
128
|
+
return {
|
|
129
|
+
from: fromFixed,
|
|
130
|
+
to,
|
|
131
|
+
matchedRule: {
|
|
132
|
+
...rule,
|
|
133
|
+
result: match,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return null;
|
|
154
140
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { OnInputEvent } from './types';
|
|
2
2
|
|
|
3
3
|
export {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
leafNodeReplacementCharacter,
|
|
5
|
+
TEXT_INPUT_RULE_TRANSACTION_KEY,
|
|
6
|
+
MAX_REGEX_MATCH,
|
|
7
7
|
} from './constants';
|
|
8
8
|
export { createInputRulePlugin } from './plugin';
|
|
9
9
|
export type { OnInputEvent };
|