feat: add sub/sup script support

This commit is contained in:
fantasticit 2022-03-26 20:05:19 +08:00
parent fd4c2de11d
commit f0e7950c5f
13 changed files with 132 additions and 1 deletions

View File

@ -39,6 +39,8 @@
"@tiptap/extension-paragraph": "^2.0.0-beta.23", "@tiptap/extension-paragraph": "^2.0.0-beta.23",
"@tiptap/extension-placeholder": "^2.0.0-beta.47", "@tiptap/extension-placeholder": "^2.0.0-beta.47",
"@tiptap/extension-strike": "^2.0.0-beta.27", "@tiptap/extension-strike": "^2.0.0-beta.27",
"@tiptap/extension-subscript": "^2.0.0-beta.10",
"@tiptap/extension-superscript": "^2.0.0-beta.10",
"@tiptap/extension-table": "^2.0.0-beta.48", "@tiptap/extension-table": "^2.0.0-beta.48",
"@tiptap/extension-table-cell": "^2.0.0-beta.20", "@tiptap/extension-table-cell": "^2.0.0-beta.20",
"@tiptap/extension-table-header": "^2.0.0-beta.22", "@tiptap/extension-table-header": "^2.0.0-beta.22",

View File

@ -0,0 +1,22 @@
import { Icon } from '@douyinfe/semi-ui';
export const IconSub: React.FC<{ style?: React.CSSProperties }> = ({ style = {} }) => {
return (
<Icon
style={style}
svg={
<svg width="16" height="16" viewBox="0 0 256 256" role="presentation">
<g stroke="currentColor" fill="none" fill-rule="evenodd">
<path stroke-width="20" stroke-linecap="round" d="m40 50 114 168M154 50 40 218"></path>
<path
d="M231.92 225.48c2.72 0 4.08-1.48 4.08-4.44 0-2.88-1.36-4.32-4.08-4.32H198.8c6.48-5.52 12.28-11.24 17.4-17.16 2.16-2.48 4.28-5.16 6.36-8.04 2.08-2.88 3.94-5.82 5.58-8.82 1.64-3 2.96-6.04 3.96-9.12 1-3.08 1.5-6.14 1.5-9.18 0-3.36-.48-6.42-1.44-9.18s-2.38-5.12-4.26-7.08-4.2-3.48-6.96-4.56c-2.76-1.08-5.9-1.62-9.42-1.62-7.92 0-13.8 2.26-17.64 6.78-3.84 4.52-5.76 10.66-5.76 18.42 0 2.24.32 3.74.96 4.5.64.76 1.96 1.14 3.96 1.14 1.76 0 2.98-.44 3.66-1.32.68-.88 1.02-2.32 1.02-4.32 0-4.8 1.04-8.74 3.12-11.82s5.4-4.62 9.96-4.62c4 0 7.16 1.32 9.48 3.96 2.32 2.64 3.48 6.12 3.48 10.44 0 3.2-.64 6.42-1.92 9.66-1.28 3.24-2.94 6.4-4.98 9.48a84.099 84.099 0 0 1-6.84 8.94c-2.52 2.88-5.04 5.6-7.56 8.16-2.52 2.56-4.92 4.94-7.2 7.14-2.28 2.2-4.18 4.1-5.7 5.7-.64 1.04-.96 2.04-.96 3-.56 1.04-.86 2.06-.9 3.06-.04 1 .1 1.88.42 2.64.32.76.8 1.38 1.44 1.86.64.48 1.36.72 2.16.72h40.2Z"
strokeWidth="5"
fill="currentColor"
fillRule="nonzero"
></path>
</g>
</svg>
}
/>
);
};

View File

@ -0,0 +1,22 @@
import { Icon } from '@douyinfe/semi-ui';
export const IconSup: React.FC<{ style?: React.CSSProperties }> = ({ style = {} }) => {
return (
<Icon
style={style}
svg={
<svg width="16" height="16" viewBox="0 0 256 256" role="presentation">
<g stroke="currentColor" fill="none" fill-rule="evenodd">
<path stroke-width="20" stroke-linecap="round" d="m40 50 114 168M154 50 40 218"></path>
<path
d="M231.92 103.48c2.72 0 4.08-1.48 4.08-4.44 0-2.88-1.36-4.32-4.08-4.32H198.8c6.48-5.52 12.28-11.24 17.4-17.16 2.16-2.48 4.28-5.16 6.36-8.04 2.08-2.88 3.94-5.82 5.58-8.82 1.64-3 2.96-6.04 3.96-9.12 1-3.08 1.5-6.14 1.5-9.18 0-3.36-.48-6.42-1.44-9.18s-2.38-5.12-4.26-7.08-4.2-3.48-6.96-4.56c-2.76-1.08-5.9-1.62-9.42-1.62-7.92 0-13.8 2.26-17.64 6.78-3.84 4.52-5.76 10.66-5.76 18.42 0 2.24.32 3.74.96 4.5.64.76 1.96 1.14 3.96 1.14 1.76 0 2.98-.44 3.66-1.32.68-.88 1.02-2.32 1.02-4.32 0-4.8 1.04-8.74 3.12-11.82s5.4-4.62 9.96-4.62c4 0 7.16 1.32 9.48 3.96 2.32 2.64 3.48 6.12 3.48 10.44 0 3.2-.64 6.42-1.92 9.66-1.28 3.24-2.94 6.4-4.98 9.48a84.099 84.099 0 0 1-6.84 8.94c-2.52 2.88-5.04 5.6-7.56 8.16-2.52 2.56-4.92 4.94-7.2 7.14-2.28 2.2-4.18 4.1-5.7 5.7-.64 1.04-.96 2.04-.96 3-.56 1.04-.86 2.06-.9 3.06-.04 1 .1 1.88.42 2.64.32.76.8 1.38 1.44 1.86.64.48 1.36.72 2.16.72h40.2Z"
strokeWidth="5"
fill="currentColor"
fillRule="nonzero"
></path>
</g>
</svg>
}
/>
);
};

View File

@ -45,3 +45,5 @@ export * from './IconHeading3';
export * from './IconTableHeaderRow'; export * from './IconTableHeaderRow';
export * from './IconTableHeaderColumn'; export * from './IconTableHeaderColumn';
export * from './IconTableHeaderCell'; export * from './IconTableHeaderCell';
export * from './IconSub';
export * from './IconSup';

View File

@ -36,6 +36,8 @@ import { SearchNReplace } from './extensions/search';
import { SelectionExtension } from './extensions/selection'; import { SelectionExtension } from './extensions/selection';
import { Status } from './extensions/status'; import { Status } from './extensions/status';
import { Strike } from './extensions/strike'; import { Strike } from './extensions/strike';
import { Subscript } from './extensions/subscript';
import { Superscript } from './extensions/superscript';
import { Table } from './extensions/table'; import { Table } from './extensions/table';
import { TableCell } from './extensions/tableCell'; import { TableCell } from './extensions/tableCell';
import { TableHeader } from './extensions/tableHeader'; import { TableHeader } from './extensions/tableHeader';
@ -89,6 +91,8 @@ export const BaseKit = [
SelectionExtension, SelectionExtension,
Status, Status,
Strike, Strike,
Subscript,
Superscript,
Table, Table,
TableCell, TableCell,
TableHeader, TableHeader,

View File

@ -0,0 +1,3 @@
import Subscript from '@tiptap/extension-subscript';
export { Subscript };

View File

@ -0,0 +1,3 @@
import Superscript from '@tiptap/extension-superscript';
export { Superscript };

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { Button } from '@douyinfe/semi-ui'; import { Button } from '@douyinfe/semi-ui';
import { IconBold, IconItalic, IconStrikeThrough, IconUnderline, IconCode } from '@douyinfe/semi-icons'; import { IconBold, IconItalic, IconStrikeThrough, IconUnderline, IconCode } from '@douyinfe/semi-icons';
import { IconSup, IconSub } from 'components/icons';
import { Tooltip } from 'components/tooltip'; import { Tooltip } from 'components/tooltip';
import { isTitleActive } from '../services/isActive'; import { isTitleActive } from '../services/isActive';
import { ColorMenu } from './color'; import { ColorMenu } from './color';
@ -62,6 +63,26 @@ export const BaseMenu: React.FC<{ editor: any }> = ({ editor }) => {
/> />
</Tooltip> </Tooltip>
<Tooltip content="上标">
<Button
theme={editor.isActive('superscript') ? 'light' : 'borderless'}
type="tertiary"
icon={<IconSup />}
onClick={() => editor.chain().focus().toggleSuperscript().run()}
disabled={isTitleActive(editor)}
/>
</Tooltip>
<Tooltip content="下标">
<Button
theme={editor.isActive('subscript') ? 'light' : 'borderless'}
type="tertiary"
icon={<IconSub />}
onClick={() => editor.chain().focus().toggleSubscript().run()}
disabled={isTitleActive(editor)}
/>
</Tooltip>
<ColorMenu editor={editor} /> <ColorMenu editor={editor} />
</> </>
); );

View File

@ -0,0 +1,13 @@
import { Mark } from './mark';
export class Subscript extends Mark {
matching() {
return this.DOMNode.nodeName === 'SUB';
}
data() {
return {
type: 'subscript',
};
}
}

View File

@ -0,0 +1,13 @@
import { Mark } from './mark';
export class Superscript extends Mark {
matching() {
return this.DOMNode.nodeName === 'SUP';
}
data() {
return {
type: 'superscript',
};
}
}

View File

@ -36,6 +36,8 @@ import { Bold } from './marks/bold';
import { Code } from './marks/code'; import { Code } from './marks/code';
import { Italic } from './marks/italic'; import { Italic } from './marks/italic';
import { Link } from './marks/link'; import { Link } from './marks/link';
import { Subscript } from './marks/subscript';
import { Superscript } from './marks/superscript';
import { Underline } from './marks/underline'; import { Underline } from './marks/underline';
export class Renderer { export class Renderer {
@ -84,7 +86,7 @@ export class Renderer {
BulletList, BulletList,
]; ];
this.marks = [Bold, Code, Italic, Link, Underline]; this.marks = [Bold, Code, Italic, Link, Subscript, Superscript, Underline];
} }
setDocument(document) { setDocument(document) {

View File

@ -22,6 +22,8 @@ import { OrderedList } from '../../../extensions/orderedList';
import { Paragraph } from '../../../extensions/paragraph'; import { Paragraph } from '../../../extensions/paragraph';
import { Status } from '../../../extensions/status'; import { Status } from '../../../extensions/status';
import { Strike } from '../../../extensions/strike'; import { Strike } from '../../../extensions/strike';
import { Subscript } from '../../../extensions/subscript';
import { Superscript } from '../../../extensions/superscript';
import { Table } from '../../../extensions/table'; import { Table } from '../../../extensions/table';
import { TableCell } from '../../../extensions/tableCell'; import { TableCell } from '../../../extensions/tableCell';
import { TableHeader } from '../../../extensions/tableHeader'; import { TableHeader } from '../../../extensions/tableHeader';
@ -67,6 +69,8 @@ const SerializerConfig = {
mixable: true, mixable: true,
expelEnclosingWhitespace: true, expelEnclosingWhitespace: true,
}, },
[Subscript.name]: { open: '<sub>', close: '</sub>', mixable: true },
[Superscript.name]: { open: '<sup>', close: '</sup>', mixable: true },
// FIXME: 如何导出 style // FIXME: 如何导出 style
[TextStyle.name]: { open: '', close: '', mixable: true, expelEnclosingWhitespace: true }, [TextStyle.name]: { open: '', close: '', mixable: true, expelEnclosingWhitespace: true },
...marks.reduce( ...marks.reduce(

View File

@ -73,6 +73,8 @@ importers:
'@tiptap/extension-paragraph': ^2.0.0-beta.23 '@tiptap/extension-paragraph': ^2.0.0-beta.23
'@tiptap/extension-placeholder': ^2.0.0-beta.47 '@tiptap/extension-placeholder': ^2.0.0-beta.47
'@tiptap/extension-strike': ^2.0.0-beta.27 '@tiptap/extension-strike': ^2.0.0-beta.27
'@tiptap/extension-subscript': ^2.0.0-beta.10
'@tiptap/extension-superscript': ^2.0.0-beta.10
'@tiptap/extension-table': ^2.0.0-beta.48 '@tiptap/extension-table': ^2.0.0-beta.48
'@tiptap/extension-table-cell': ^2.0.0-beta.20 '@tiptap/extension-table-cell': ^2.0.0-beta.20
'@tiptap/extension-table-header': ^2.0.0-beta.22 '@tiptap/extension-table-header': ^2.0.0-beta.22
@ -150,6 +152,8 @@ importers:
'@tiptap/extension-paragraph': 2.0.0-beta.23_@tiptap+core@2.0.0-beta.171 '@tiptap/extension-paragraph': 2.0.0-beta.23_@tiptap+core@2.0.0-beta.171
'@tiptap/extension-placeholder': 2.0.0-beta.47_@tiptap+core@2.0.0-beta.171 '@tiptap/extension-placeholder': 2.0.0-beta.47_@tiptap+core@2.0.0-beta.171
'@tiptap/extension-strike': 2.0.0-beta.27_@tiptap+core@2.0.0-beta.171 '@tiptap/extension-strike': 2.0.0-beta.27_@tiptap+core@2.0.0-beta.171
'@tiptap/extension-subscript': 2.0.0-beta.10_@tiptap+core@2.0.0-beta.171
'@tiptap/extension-superscript': 2.0.0-beta.10_@tiptap+core@2.0.0-beta.171
'@tiptap/extension-table': 2.0.0-beta.48_@tiptap+core@2.0.0-beta.171 '@tiptap/extension-table': 2.0.0-beta.48_@tiptap+core@2.0.0-beta.171
'@tiptap/extension-table-cell': 2.0.0-beta.20_@tiptap+core@2.0.0-beta.171 '@tiptap/extension-table-cell': 2.0.0-beta.20_@tiptap+core@2.0.0-beta.171
'@tiptap/extension-table-header': 2.0.0-beta.22_@tiptap+core@2.0.0-beta.171 '@tiptap/extension-table-header': 2.0.0-beta.22_@tiptap+core@2.0.0-beta.171
@ -1807,6 +1811,22 @@ packages:
'@tiptap/core': 2.0.0-beta.171 '@tiptap/core': 2.0.0-beta.171
dev: false dev: false
/@tiptap/extension-subscript/2.0.0-beta.10_@tiptap+core@2.0.0-beta.171:
resolution: {integrity: sha512-er8/1lp0Rb+SKwEioW0w4oVf3EkdQZ0WS/5kPBG4W0DncfUMT+bw5de76S3kRL9PLZ9UShAL7wuXtuiSi5QsMw==}
peerDependencies:
'@tiptap/core': ^2.0.0-beta.1
dependencies:
'@tiptap/core': 2.0.0-beta.171
dev: false
/@tiptap/extension-superscript/2.0.0-beta.10_@tiptap+core@2.0.0-beta.171:
resolution: {integrity: sha512-TUUBS8XsD2MorGORYVlhGDH7wcc9diSbHscD4Dnz8pKWVR0JPUd/od4h5qSffDzAOKxtphTiX9LOFWk6zVooKg==}
peerDependencies:
'@tiptap/core': ^2.0.0-beta.1
dependencies:
'@tiptap/core': 2.0.0-beta.171
dev: false
/@tiptap/extension-table-cell/2.0.0-beta.20_@tiptap+core@2.0.0-beta.171: /@tiptap/extension-table-cell/2.0.0-beta.20_@tiptap+core@2.0.0-beta.171:
resolution: {integrity: sha512-IllQyxLQvgm1FAewz3U+DkgNHRthmuVrtUQnG6la45qdUOLCOrpFbRRaQ1LJ/BpbvZ2Xs1o2yAa97BqZOPwovQ==} resolution: {integrity: sha512-IllQyxLQvgm1FAewz3U+DkgNHRthmuVrtUQnG6la45qdUOLCOrpFbRRaQ1LJ/BpbvZ2Xs1o2yAa97BqZOPwovQ==}
peerDependencies: peerDependencies: