Skip to content

Commit

Permalink
Added auto corrections and punctuation
Browse files Browse the repository at this point in the history
  • Loading branch information
umerfaruk committed Mar 5, 2024
1 parent 7b41abc commit a1db377
Show file tree
Hide file tree
Showing 11 changed files with 1,398 additions and 31 deletions.
1,096 changes: 1,096 additions & 0 deletions demo/src/autoCorrection.js

Large diffs are not rendered by default.

20 changes: 19 additions & 1 deletion demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import { Button, ConfigProvider, Divider, Drawer, FloatButton, Select, Space, Sw
import Icons from "../../src/icons";

import i18n from "../../src/i18n";
import punctuationCorrections from "./punctuationCorrections";
import wordList from './wordList';
import autoCorrection from './autoCorrection';

const Demo = () => {
const [open, setOpen] = useState(false);
const [value, setValue] = useState("# this is heading\n\nThis is a paragraph");
const [configuration, setConfiguration] = useState({
richText: true,
language: "en",
language: "ur",
toolbar: {
showAlignment: true,
showBlockFormat: true,
Expand All @@ -25,6 +28,12 @@ const Demo = () => {
showInsertLink: true,
showSave: true,
},
spellchecker: {
enabled : true,
punctuationCorrections: (lang) => punctuationCorrections[lang],
autoCorrections: (lang) => autoCorrection[lang],
wordList : (lang) => wordList[lang]
},
onSave: (contents) => console.log(contents),
format: "raw",
});
Expand Down Expand Up @@ -157,6 +166,15 @@ const Demo = () => {
}
/>
<Divider />
<Switch
checkedChildren="Spell Check"
unCheckedChildren="No Spell Check"
defaultChecked={configuration.spellchecker.enabled}
onChange={(checked) =>
setConfiguration((e) => ({ ...e, spellchecker: {... e.spellchecker, enabled : checked } }))
}
/>
<Divider />
<Button onClick={() => setValue(Date.now())}>
Change Value
</Button>
Expand Down
38 changes: 38 additions & 0 deletions demo/src/punctuationCorrections.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export default {
"ur" : [
{ incorrectText: ".", correctText: "۔", completeWord: false },
{ incorrectText: ",", correctText: "،", completeWord: false },
{ incorrectText: " ۔", correctText: "۔", completeWord: false },
{ incorrectText: " - ", correctText: "۔ ", completeWord: false },
{ incorrectText: " ،", correctText: "،", completeWord: false },
{ incorrectText: "?", correctText: "؟", completeWord: false },
{ incorrectText: " ؟", correctText: "؟", completeWord: false },
{ incorrectText: " !", correctText: "!", completeWord: false },
{ incorrectText: "( ", correctText: "(", completeWord: false },
{ incorrectText: " (", correctText: "(", completeWord: false },
{ incorrectText: "ه", correctText: "ہ", completeWord: false },
{ incorrectText: "ک", correctText: "ک", completeWord: false },
{ incorrectText: "ئو", correctText: "ؤ", completeWord: false },
{ incorrectText: "’’", correctText: "”", completeWord: false },
{ incorrectText: "‘‘", correctText: "“", completeWord: false },
{ incorrectText: "…", correctText: "۔۔۔", completeWord: false },
{ incorrectText: "——", correctText: "۔۔۔", completeWord: false },
{ incorrectText: "—", correctText: "۔۔۔", completeWord: false },
{ incorrectText: " ۔۔۔", correctText: "۔۔۔ ", completeWord: false },
{ incorrectText: "،،", correctText: "“", completeWord: false },
{ incorrectText: '۔"', correctText: "۔“", completeWord: false },
{ incorrectText: '، "', correctText: "۔ ”", completeWord: false },
{ incorrectText: '؟"', correctText: "؟“", completeWord: false },
{ incorrectText: '!"', correctText: "!“", completeWord: false },
{ incorrectText: "-", correctText: "۔", completeWord: false },
{ incorrectText: " ، ", correctText: "، ", completeWord: false },
{ incorrectText: " ۔", correctText: "۔ ", completeWord: false },
{ incorrectText: " : ", correctText: ": ", completeWord: false },
{ incorrectText: "آ", correctText: "آ", completeWord: false },
{ incorrectText: "ؤ", correctText: "ؤ", completeWord: false },
{ incorrectText: " ِ", correctText: "ِ", completeWord: false },
{ incorrectText: " ً", correctText: "ً", completeWord: false },
],
"en": [
{ incorrectText: " ۔ ", correctText: "ً۔ ", completeWord: false },
]};
1 change: 1 addition & 0 deletions demo/src/wordList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default { "ur" : [], "en" : [] };
7 changes: 7 additions & 0 deletions src/commands/spellCheckCommand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {
createCommand,
} from 'lexical';

export const SPELLCHECK_COMMAND = createCommand('SPELLCHECK_COMMAND');
export const AUTO_CORRECT_COMMAND = createCommand('AUTO_CORRECT_COMMAND');
export const PUNCTUATION_CORRECT_COMMAND = createCommand('PUNCTUATION_CORRECT_COMMAND');
8 changes: 8 additions & 0 deletions src/i18n/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export default {
horizontalRule: "Horizontal Rule",
image: "Insert Image",
save: "Save",
spellcheck: "Spellcheck",
punctuation: "Punctuation",
autoCorrect: "Auto Correct",
tools: "Tools",
},
},
'ur': {
Expand Down Expand Up @@ -73,6 +77,10 @@ export default {
horizontalRule: "افقی قاعدہ",
image: "تصویر شامل کریں",
save: "محفوظ کریں",
spellcheck: "تصحیح",
punctuation: "املا",
autoCorrect: "خودکار تصحیح",
tools: "آلات",
}
}
}
6 changes: 5 additions & 1 deletion src/icons/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 11 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,9 @@ import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
import { CheckListPlugin } from '@lexical/react/LexicalCheckListPlugin';
import { ListPlugin } from '@lexical/react/LexicalListPlugin';
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary";
import {
$convertFromMarkdownString,
$convertToMarkdownString,
TRANSFORMERS,
} from '@lexical/markdown';
import { MarkdownShortcutPlugin } from '@lexical/react/LexicalMarkdownShortcutPlugin';
Expand All @@ -35,6 +32,7 @@ import ContentEditable from './ui/contentEditable';
import i18n from './i18n';
import styles from './styles.module.css'; // Import css modules stylesheet as styles
import { SavePlugin } from './commands/saveCommand';
import SpellCheckerPlugin from './plugins/spellchecker';

// ------------------------------------------------------
const EMPTY_CONTENT =
Expand Down Expand Up @@ -69,13 +67,19 @@ export default ({ value = EMPTY_CONTENT,
richText : false,
toolbar : {
fonts : null,
fontSizes: null
fontSizes: null,
},
onSave: () => {},
language : "en",
languageTools: false,
placeholder : null,
format: "raw"
format: "raw",
spellchecker : {
enabled: false,
punctuationCorrections: () => [],
autoCorrections: () => [],
wordList : () => [],
}
}
}) => {
const locale = i18n[configuration.language];
Expand Down Expand Up @@ -117,7 +121,7 @@ export default ({ value = EMPTY_CONTENT,
return (
<div className={ isRtl ? styles.rtl : null }>
<LexicalComposer initialConfig={initialConfig}>
{ configuration.richText && <ToolbarPlugin configuration={configuration.toolbar} setIsLinkEditMode={setIsLinkEditMode} locale={locale} /> }
{ configuration.richText && <ToolbarPlugin configuration={configuration} setIsLinkEditMode={setIsLinkEditMode} locale={locale} /> }
{ configuration.richText ? <>
<RichTextPlugin
contentEditable={
Expand Down Expand Up @@ -163,6 +167,7 @@ export default ({ value = EMPTY_CONTENT,
<HistoryPlugin />
<MyCustomAutoFocusPlugin />
{ configuration?.toolbar?.showSave && <SavePlugin onSave={configuration.onSave} /> }
<SpellCheckerPlugin locale={locale} language={configuration.language} configuration={configuration.spellchecker} />
<ControlledValuePlugin
value={value}
onChange={onChange}
Expand Down
141 changes: 141 additions & 0 deletions src/plugins/spellchecker/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {
$getRoot,
COMMAND_PRIORITY_LOW,
} from 'lexical';
import {useEffect, useState} from 'react';

import { AUTO_CORRECT_COMMAND, SPELLCHECK_COMMAND, PUNCTUATION_CORRECT_COMMAND } from "../../commands/spellCheckCommand";
import { Drawer } from 'antd';

const getReplaceAllRegex = (corrections) => {
let retVal = '';
corrections.forEach((c) => {
// retVal += `(${c.completeWord ? `\\b${c.incorrectText.trim()}\\b` : c.incorrectText.trim()})|`;
retVal += `(${c.incorrectText.trim()})|`;
});

// return new RegExp(retVal.slice(0, -1), 'gimu');
return new RegExp(`\\b${retVal.slice(0, -1)}\\b`, 'giu');
};
const correctPunctuations = (punctuationCorrections, text) => {
text = text.replace(/ +/g, ' ');
punctuationCorrections.forEach((c) => {
text = text.replaceAll(c.completeWord ? `${c.incorrectText}\\b` : c.incorrectText, c.correctText);
});
return text;
};

const autoCorrectText = (autoCorrections, text) => {
const correctionRegex = getReplaceAllRegex(autoCorrections);
return text.replaceAll(correctionRegex, (matched) => autoCorrections.find((o) => o.incorrectText === matched)?.correctText.trim());
};

export default function SpellCheckerPlugin({ locale, language, configuration = { enabled : false} }) {
if (!configuration.enabled) return null;
const [editor] = useLexicalComposerContext();
const [open, setOpen] = useState(false);

const onClose = () => {
setOpen(false);
};

useEffect(() => {
editor.registerCommand(
SPELLCHECK_COMMAND,
() => {
setOpen(true);
},
COMMAND_PRIORITY_LOW,
);

/* automatic correction */

editor.registerCommand(
AUTO_CORRECT_COMMAND,
() => {
autoCorrect();
},
COMMAND_PRIORITY_LOW,
);

const autoCorrectNode = (node, corrections) => {
if (node.getChildren) {
node.getChildren().map((child) => {
autoCorrectNode(child, corrections);
});
}

if (node.getType() === 'text') {
node.setTextContent(autoCorrectText(corrections, node.getTextContent()));
}

return node
}

const autoCorrect = () => {
var corrections = configuration.autoCorrections(language);
editor.update(() => {
var root = $getRoot(editor);
var children = root.getChildren();
children.forEach((child) => {
autoCorrectNode(child, corrections);
});
});
}
/* automatic correction ends */

/* Punctuation correction */
editor.registerCommand(
PUNCTUATION_CORRECT_COMMAND,
() => {
punctuationCorrection();
},
COMMAND_PRIORITY_LOW,
);

}, [editor]);


const punctuationCorrectionNode = (node, corrections) => {
if (node.getChildren) {
node.getChildren().map((child) => {
punctuationCorrectionNode(child, corrections);
});
}

if (node.getType() === 'text') {
node.setTextContent(correctPunctuations(corrections, node.getTextContent()));
}

return node
}

const punctuationCorrection = () => {
var corrections = configuration.punctuationCorrections(language);
editor.update(() => {
var root = $getRoot(editor);
var children = root.getChildren();
children.forEach((child) => {
punctuationCorrectionNode(child, corrections);
});
});
}

/* Punctuation correction ends */

return (
<>
<Drawer
title={locale?.resources?.spellchecker}
placement="right"
closable={true}
onClose={onClose}
open={open}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Drawer>
</>);
}
Loading

0 comments on commit a1db377

Please sign in to comment.