Skip to content

Commit 752042e

Browse files
committed
fix(TextDirection): Adjust to our code style
Signed-off-by: Jonas <[email protected]>
1 parent b430d57 commit 752042e

File tree

1 file changed

+161
-144
lines changed

1 file changed

+161
-144
lines changed

src/extensions/TextDirection.ts

Lines changed: 161 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,174 @@
1-
import { Extension } from "@tiptap/core";
2-
import { Plugin, PluginKey } from "@tiptap/pm/state";
3-
4-
const RTL = "\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC";
5-
const LTR =
6-
"A-Za-z\u00C0-\u00D6\u00D8-\u00F6" +
7-
"\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u200E\u2C00-\uFB1C" +
8-
"\uFE00-\uFE6F\uFEFD-\uFFFF";
9-
10-
const RTL_REGEX = new RegExp("^[^" + LTR + "]*[" + RTL + "]");
11-
const LTR_REGEX = new RegExp("^[^" + RTL + "]*[" + LTR + "]");
12-
13-
// Source: https://github.com/facebook/lexical/blob/429e3eb5b5a244026fa4776650aabe3c8e17536b/packages/lexical/src/LexicalUtils.ts#L163
14-
export function getTextDirection(text: string): "ltr" | "rtl" | null {
15-
if (text.length == 0) {
16-
return null;
17-
}
18-
if (RTL_REGEX.test(text)) {
19-
return "rtl";
20-
}
21-
if (LTR_REGEX.test(text)) {
22-
return "ltr";
23-
}
24-
return null;
1+
/**
2+
* SPDX-FileCopyrightText: 2023 Amir Hossein Hashemi
3+
* SPDX-License-Identifier: MIT
4+
*/
5+
6+
import { Extension } from '@tiptap/core'
7+
import { Plugin, PluginKey } from '@tiptap/pm/state'
8+
9+
const RTL = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC'
10+
const LTR = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6'
11+
+ '\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u200E\u2C00-\uFB1C'
12+
+ '\uFE00-\uFE6F\uFEFD-\uFFFF'
13+
14+
/* eslint-disable no-misleading-character-class */
15+
const RTL_REGEX = new RegExp('^[^' + LTR + ']*[' + RTL + ']')
16+
const LTR_REGEX = new RegExp('^[^' + RTL + ']*[' + LTR + ']')
17+
18+
/**
19+
* @param text Text string
20+
*
21+
*Source: https://github.com/facebook/lexical/blob/429e3eb5b5a244026fa4776650aabe3c8e17536b/packages/lexical/src/LexicalUtils.ts#L163
22+
*/
23+
export function getTextDirection(text: string): 'ltr' | 'rtl' | null {
24+
if (text.length === 0) {
25+
return null
26+
}
27+
if (RTL_REGEX.test(text)) {
28+
return 'rtl'
29+
}
30+
if (LTR_REGEX.test(text)) {
31+
return 'ltr'
32+
}
33+
return null
2534
}
2635

27-
const validDirections = ["ltr", "rtl", "auto"] as const;
36+
const validDirections = ['ltr', 'rtl', 'auto'] as const
2837

29-
type Direction = (typeof validDirections)[number];
38+
type Direction = (typeof validDirections)[number]
3039

40+
/**
41+
* @param object Property object
42+
* @param object.types List of node types to consider
43+
*/
3144
function TextDirectionPlugin({ types }: { types: string[] }) {
32-
return new Plugin({
33-
key: new PluginKey("textDirection"),
34-
appendTransaction: (transactions, oldState, newState) => {
35-
const docChanges = transactions.some(
36-
(transaction) => transaction.docChanged,
37-
);
38-
if (!docChanges) {
39-
return;
40-
}
41-
42-
let modified = false;
43-
const tr = newState.tr;
44-
tr.setMeta("addToHistory", false);
45-
46-
newState.doc.descendants((node, pos) => {
47-
if (types.includes(node.type.name)) {
48-
if (node.attrs.dir !== null && node.textContent.length > 0) {
49-
return;
50-
}
51-
const marks = tr.storedMarks || [];
52-
tr.setNodeAttribute(pos, "dir", getTextDirection(node.textContent));
53-
// `tr.setNodeAttribute` resets the stored marks so we'll restore them
54-
for (const mark of marks) {
55-
tr.addStoredMark(mark);
56-
}
57-
modified = true;
58-
}
59-
});
60-
61-
return modified ? tr : null;
62-
},
63-
});
45+
return new Plugin({
46+
key: new PluginKey('textDirection'),
47+
appendTransaction: (transactions, oldState, newState) => {
48+
const docChanges = transactions.some(
49+
(transaction) => transaction.docChanged,
50+
)
51+
if (!docChanges) {
52+
return
53+
}
54+
55+
let modified = false
56+
const tr = newState.tr
57+
tr.setMeta('addToHistory', false)
58+
59+
newState.doc.descendants((node, pos) => {
60+
if (types.includes(node.type.name)) {
61+
if (node.attrs.dir !== null && node.textContent.length > 0) {
62+
return
63+
}
64+
const marks = tr.storedMarks || []
65+
tr.setNodeAttribute(
66+
pos,
67+
'dir',
68+
getTextDirection(node.textContent),
69+
)
70+
// `tr.setNodeAttribute` resets the stored marks so we'll restore them
71+
for (const mark of marks) {
72+
tr.addStoredMark(mark)
73+
}
74+
modified = true
75+
}
76+
})
77+
78+
return modified ? tr : null
79+
},
80+
})
6481
}
6582

66-
declare module "@tiptap/core" {
67-
interface Commands<ReturnType> {
68-
textDirection: {
69-
/**
70-
* Set the text direction attribute
71-
*/
72-
setTextDirection: (direction: Direction) => ReturnType;
73-
/**
74-
* Unset the text direction attribute
75-
*/
76-
unsetTextDirection: () => ReturnType;
77-
};
78-
}
83+
declare module '@tiptap/core' {
84+
interface Commands<ReturnType> {
85+
textDirection: {
86+
/**
87+
* Set the text direction attribute
88+
*/
89+
setTextDirection: (direction: Direction) => ReturnType
90+
/**
91+
* Unset the text direction attribute
92+
*/
93+
unsetTextDirection: () => ReturnType
94+
}
95+
}
7996
}
8097

8198
export interface TextDirectionOptions {
82-
types: string[];
83-
defaultDirection: Direction | null;
99+
types: string[]
100+
defaultDirection: Direction | null
84101
}
85102

86103
export const TextDirection = Extension.create<TextDirectionOptions>({
87-
name: "textDirection",
88-
89-
addOptions() {
90-
return {
91-
types: [],
92-
defaultDirection: null,
93-
};
94-
},
95-
96-
addGlobalAttributes() {
97-
return [
98-
{
99-
types: this.options.types,
100-
attributes: {
101-
dir: {
102-
default: null,
103-
parseHTML: (element) =>
104-
element.dir || this.options.defaultDirection,
105-
renderHTML: (attributes) => {
106-
if (attributes.dir === this.options.defaultDirection) {
107-
return {};
108-
}
109-
return { dir: attributes.dir };
110-
},
111-
},
112-
},
113-
},
114-
];
115-
},
116-
117-
addCommands() {
118-
return {
119-
setTextDirection:
120-
(direction: Direction) =>
121-
({ commands }) => {
122-
if (!validDirections.includes(direction)) {
123-
return false;
124-
}
125-
126-
return this.options.types.every((type) =>
127-
commands.updateAttributes(type, { dir: direction }),
128-
);
129-
},
130-
131-
unsetTextDirection:
132-
() =>
133-
({ commands }) => {
134-
return this.options.types.every((type) =>
135-
commands.resetAttributes(type, "dir"),
136-
);
137-
},
138-
};
139-
},
140-
141-
addKeyboardShortcuts() {
142-
return {
143-
"Mod-Alt-l": () => this.editor.commands.setTextDirection("ltr"),
144-
"Mod-Alt-r": () => this.editor.commands.setTextDirection("rtl"),
145-
};
146-
},
147-
148-
addProseMirrorPlugins() {
149-
return [
150-
TextDirectionPlugin({
151-
types: this.options.types,
152-
}),
153-
];
154-
},
155-
});
156-
157-
export default TextDirection;
104+
name: 'textDirection',
105+
106+
addOptions() {
107+
return {
108+
types: [],
109+
defaultDirection: null,
110+
}
111+
},
112+
113+
addGlobalAttributes() {
114+
return [
115+
{
116+
types: this.options.types,
117+
attributes: {
118+
dir: {
119+
default: null,
120+
parseHTML: (element) =>
121+
element.dir || this.options.defaultDirection,
122+
renderHTML: (attributes) => {
123+
if (attributes.dir === this.options.defaultDirection) {
124+
return {}
125+
}
126+
return { dir: attributes.dir }
127+
},
128+
},
129+
},
130+
},
131+
]
132+
},
133+
134+
addCommands() {
135+
return {
136+
setTextDirection:
137+
(direction: Direction) =>
138+
({ commands }) => {
139+
if (!validDirections.includes(direction)) {
140+
return false
141+
}
142+
143+
return this.options.types.every((type) =>
144+
commands.updateAttributes(type, { dir: direction }),
145+
)
146+
},
147+
148+
unsetTextDirection:
149+
() =>
150+
({ commands }) => {
151+
return this.options.types.every((type) =>
152+
commands.resetAttributes(type, 'dir'),
153+
)
154+
},
155+
}
156+
},
157+
158+
addKeyboardShortcuts() {
159+
return {
160+
'Mod-Alt-l': () => this.editor.commands.setTextDirection('ltr'),
161+
'Mod-Alt-r': () => this.editor.commands.setTextDirection('rtl'),
162+
}
163+
},
164+
165+
addProseMirrorPlugins() {
166+
return [
167+
TextDirectionPlugin({
168+
types: this.options.types,
169+
}),
170+
]
171+
},
172+
})
173+
174+
export default TextDirection

0 commit comments

Comments
 (0)