Skip to content

Commit b430d57

Browse files
committed
chore: Use local copy of tiptap-text-direction extension
Signed-off-by: Jonas <[email protected]>
1 parent 33d0fe2 commit b430d57

File tree

4 files changed

+158
-19
lines changed

4 files changed

+158
-19
lines changed

package-lock.json

Lines changed: 0 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@
102102
"proxy-polyfill": "^0.3.2",
103103
"slug": "^9.1.0",
104104
"tippy.js": "^6.3.7",
105-
"tiptap-text-direction": "^0.3.2",
106105
"uuid": "^10.0.0",
107106
"vue": "^2.7.16",
108107
"vue-click-outside": "^1.1.0",

src/extensions/RichText.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import TaskItem from './../nodes/TaskItem.js'
4141
import TaskList from './../nodes/TaskList.js'
4242
import Text from '@tiptap/extension-text'
4343
import TrailingNode from './../nodes/TrailingNode.js'
44-
import TextDirection from 'tiptap-text-direction'
44+
import TextDirection from './../extensions/TextDirection.ts'
4545
/* eslint-enable import/no-named-as-default */
4646

4747
import { Strong, Italic, Strike, Link, Underline } from './../marks/index.js'

src/extensions/TextDirection.ts

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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;
25+
}
26+
27+
const validDirections = ["ltr", "rtl", "auto"] as const;
28+
29+
type Direction = (typeof validDirections)[number];
30+
31+
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+
});
64+
}
65+
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+
}
79+
}
80+
81+
export interface TextDirectionOptions {
82+
types: string[];
83+
defaultDirection: Direction | null;
84+
}
85+
86+
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;

0 commit comments

Comments
 (0)