Skip to content

Commit

Permalink
feat: create tiptap language extension
Browse files Browse the repository at this point in the history
  • Loading branch information
AliKdhim87 committed Aug 11, 2023
1 parent 8125607 commit 2076ad1
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Option, Select } from '@strapi/design-system/Select';
import type { Editor as EditorTypes } from '@tiptap/react';
import React from 'react';
import { useIntl } from 'react-intl';
import getTrad from '../../utils/getTrad';

interface LanguagesListProps {
editor: EditorTypes;
languages: { titleKey: string; languageCode: string }[];
selectField: {
placeholder?: string;
removeLanguageOption: string;
};
}

export const LanguagesList = ({ editor, languages, selectField }: LanguagesListProps) => {
const { formatMessage } = useIntl();
return (
<Select
size="S"
placeholder={selectField?.placeholder}
value={editor.isActive('language') ? editor.getAttributes('language').lang : ''}
onChange={(event: string | number) => {
if (editor) {
if (event === 'remove_language') {
editor.chain().focus().unsetLanguage().run();
return;
}
editor
.chain()
.focus()
.toggleLanguage({ lang: event as string })
.run();
}
}}
>
<Option value="remove_language">{selectField.removeLanguageOption}</Option>
{languages &&
languages.length > 0 &&
languages.map(({ languageCode, titleKey }) => (
<Option key={languageCode} className="icon" value={languageCode}>
{formatMessage({ id: getTrad(titleKey) })}
</Option>
))}
</Select>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ import {
import { FaImage } from 'react-icons/fa';
import { GrBlockQuote } from 'react-icons/gr';
import { VscTable } from 'react-icons/vsc';
import { useIntl } from 'react-intl';
import { LanguagesList } from './LanguagesList';
import { PriceList } from './PriceList';
import defaultSettings from '../../../../utils/defaults';
import { useLink } from '../../hooks/useLink';
import getTrad from '../../utils/getTrad';
import { LinkDialog } from '../LinkDialog';
import { PriceListTypes } from '../extensions/Price/index';
import initialTableWithCaption from '../extensions/schema/initialTableWithCaptionData';
Expand All @@ -55,6 +58,12 @@ interface LinkToolbarProps {
onClick: (_event: React.MouseEvent<HTMLButtonElement>) => void;
}

export const languages = [
{ titleKey: 'languages.dutch', languageCode: 'nl' },
{ titleKey: 'languages.english', languageCode: 'en' },
{ titleKey: 'languages.arabic', languageCode: 'ar' },
];

export const LinkToolbar = ({ editor, onClick }: LinkToolbarProps) => {
return (
<IconButton
Expand Down Expand Up @@ -98,7 +107,7 @@ export const Toolbar = ({ editor, toggleMediaLib, settings, productPrice }: Tool
const [youTubeInput, setYouTubeInput] = useState('');
const [youTubeHeightInput, setYouTubeHeightInput] = useState(settings.youtube.height);
const [youTubeWidthInput, setYouTubeWidthInput] = useState(settings.youtube.width);

const { formatMessage } = useIntl();
const { observe, inView } = useInView({
rootMargin: '-1px 0px 0px 0px',
threshold: [1],
Expand Down Expand Up @@ -195,12 +204,27 @@ export const Toolbar = ({ editor, toggleMediaLib, settings, productPrice }: Tool
<PriceList editor={editor} productPrice={productPrice} />
</Box>
)}
{settings.other.language && (
<Box className="button-group">
<LanguagesList
editor={editor}
languages={languages}
selectField={{
placeholder: formatMessage({
id: getTrad('components.languagesList.placeholder'),
defaultMessage: 'Select a language',
}),
removeLanguageOption: formatMessage({ id: getTrad('components.languagesList.removeLanguage') }),
}}
/>
</Box>
)}
<IconButtonGroup className="button-group">
{settings.bold ? (
<IconButton
icon={<Bold />}
label="Bold"
className={['large-icon', editor.isActive('bold') ? 'is-active' : '']}
className={['large-icon', editor.isActive('bold') ? 'is-active' : ''].join(' ')}
onClick={() => editor.chain().focus().toggleBold().run()}
/>
) : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { mergeDeep } from '../../utils/merge';
import Editor from '../Editor';
import { Figcaption } from '../extensions/Figcaption/index';
import { Figure } from '../extensions/Figure/index';
import { Language } from '../extensions/Language';
import { LeadParagraph } from '../extensions/LeadParagraph/index';
import { Price } from '../extensions/Price/index';

Expand Down Expand Up @@ -133,6 +134,7 @@ const WysiwygContent = ({
BulletListExtension,
HeadingExtension,
LeadParagraph,
settings.other.language ? Language : null,
Price,
Figcaption,
Figure.extend({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/* eslint-disable no-unused-vars */
import { Mark, markInputRule, markPasteRule, mergeAttributes } from '@tiptap/core';

export interface LanguageOptions {
HTMLAttributes: Record<string, any>;
}

declare module '@tiptap/core' {
interface Commands<ReturnType> {
language: {
/**
* Set the language attribute
*/
setLanguage: (attributes?: { lang: string }) => ReturnType;
/**
* Toggle the language attribute
*/
toggleLanguage: (attributes?: { lang: string }) => ReturnType;
/**
* Unset the language attribute
*/
unsetLanguage: () => ReturnType;
};
}
}

export const inputRegex = /(?:^|\s)((?:==)((?:[^~=]+))(?:==))$/;
export const pasteRegex = /(?:^|\s)((?:==)((?:[^~=]+))(?:==))/g;

export const Language = Mark.create<LanguageOptions>({
name: 'language',

addOptions() {
return {
HTMLAttributes: {},
};
},

addAttributes() {
return {
lang: {
default: null,
parseHTML: (element) => {
return element.getAttribute('lang') || null;
},
renderHTML: (attributes) => {
if (!attributes.lang) {
return {};
}
return {
lang: attributes.lang,
dir: getDirectionFromLanguageCode(attributes.lang),
};
},
},
};
},

parseHTML() {
return [
{
tag: 'span[lang][dir]',
},
];
},

renderHTML({ HTMLAttributes }) {
return ['span', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
},

addCommands() {
return {
setHighlight:
(attributes) =>
({ commands }) => {
return commands.setMark(this.name, attributes);
},
toggleLanguage:
(attributes) =>
({ commands }) => {
return commands.toggleMark(this.name, attributes);
},
unsetLanguage:
() =>
({ commands }) => {
return commands.unsetMark(this.name);
},
};
},

addInputRules() {
return [
markInputRule({
find: inputRegex,
type: this.type,
}),
];
},

addPasteRules() {
return [
markPasteRule({
find: pasteRegex,
type: this.type,
}),
];
},
});

// TODO create utils package and move this logic there
function getDirectionFromLanguageCode(languageCode: string): 'ltr' | 'rtl' {
// List of language codes that are written right-to-left
const rtlLanguages = [
'ar',
'he',
'fa',
'ur',
'yi',
'dv',
'ps',
'ku',
'ug',
'arc',
'azb',
'mzn',
'pnb',
'sd',
'ckb',
'lrc',
'glk',
'nv',
'prs',
'tmr',
'uga',
];
// Check if the language code is in the list of RTL languages
if (rtlLanguages.includes(languageCode.toLowerCase())) {
return 'rtl';
}

// Default to left-to-right if not in the list of RTL languages
return 'ltr';
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface EditorOptions {
other: {
wordcount: boolean;
saveJson: boolean;
language: boolean;
};
youtube: {
enabled: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,27 @@ const Other: React.FC<OtherProps> = ({ values, handleChange }) => {
/>
</Box>
</GridLayout>
<GridLayout>
<Box>
<ToggleInput
label="Language"
hint="Apply language attributes to text"
size="S"
name="other.language"
onLabel="Enabled"
offLabel="Disabled"
checked={values.other.language}
onChange={() =>
handleChange({
target: {
name: 'other.language',
value: !values.other.language,
},
})
}
/>
</Box>
</GridLayout>

<Box marginTop="2rem" marginBottom="1rem" />

Expand Down
8 changes: 7 additions & 1 deletion packages/strapi-tiptap-editor/admin/src/translations/en.json
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
{}
{
"components.languagesList.placeholder": "Select a language",
"components.languagesList.removeLanguage": "Remove Language",
"languages.dutch": "Dutch",
"languages.english": "English",
"languages.arabic": "Arabic"
}

This file was deleted.

7 changes: 7 additions & 0 deletions packages/strapi-tiptap-editor/admin/src/translations/nl.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"components.languagesList.placeholder": "Selecteer een taal",
"components.languagesList.removeLanguage": "Taal verwijderen",
"languages.dutch": "Nederlands",
"languages.english": "Engels",
"languages.arabic": "Arabisch"
}
1 change: 1 addition & 0 deletions packages/strapi-tiptap-editor/utils/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default {
},
other: {
wordcount: false,
language: true,
saveJson: false,
},
youtube: {
Expand Down

0 comments on commit 2076ad1

Please sign in to comment.