feat: tailchat-design增加富文本编辑器

pull/56/head
moonrailgun 3 years ago
parent bf551557c1
commit 6177899b5f

@ -1,4 +1,3 @@
import { useMemoizedFn } from 'node_modules/tailchat-shared';
import React, {
PropsWithChildren,
useEffect,
@ -6,6 +5,7 @@ import React, {
useRef,
useState,
} from 'react';
import { useMemoizedFn } from 'ahooks';
interface AutoFolderProps extends PropsWithChildren {
maxHeight: number;

@ -0,0 +1,51 @@
import { isArray, ObjectMark, RemirrorJSON } from 'remirror';
/**
* BBCode
*/
export function transformToBBCode(json: RemirrorJSON): string {
if (json.type === 'doc') {
return (json.content ?? []).map(transformToBBCode).join('\n');
}
if (json.type === 'paragraph') {
return (json.content ?? []).map(transformToBBCode).join('');
}
if (json.type === 'text') {
let text = json.text ?? '';
if (isArray(json.marks)) {
(json.marks ?? []).forEach((mark) => {
if (typeof mark === 'string') {
mark = { type: mark };
}
text = applyMarks(mark, text);
});
}
return text;
}
return '';
}
/**
* textmark
*/
function applyMarks(mark: ObjectMark, text: string): string {
if (mark.type === 'bold') {
return `[b]${text}[/b]`;
}
if (mark.type === 'underline') {
return `[u]${text}[/u]`;
}
if (mark.type === 'italic') {
return `[i]${text}[/i]`;
}
if (mark.type === 'code') {
return `[code]${text}[/code]`;
}
return text;
}

@ -0,0 +1,12 @@
.remirror-editor-wrapper {
height: 100%;
}
.tailchat-rich-editor {
height: 100%;
outline: 0;
}
.tailchat-rich-editor p {
margin: 0;
}

@ -0,0 +1,44 @@
import React from 'react';
import {
Remirror,
useRemirror,
OnChangeJSON,
EditorComponent,
} from '@remirror/react';
import { useMemoizedFn } from 'ahooks';
import type { RemirrorJSON } from 'remirror';
import { Toolbar } from './toolbar';
import { extensions } from './extensions';
import { transformToBBCode } from './bbcode';
import './editor.css';
interface RichEditorProps extends React.PropsWithChildren {
initContent: string;
onChange: (bbcode: string) => void;
}
export const RichEditor: React.FC<RichEditorProps> = React.memo((props) => {
const { manager, state } = useRemirror({
extensions,
content: props.initContent,
stringHandler: 'html',
selection: 'end',
});
const handleChange = useMemoizedFn((json: RemirrorJSON) => {
props.onChange(transformToBBCode(json));
});
return (
<Remirror
classNames={['tailchat-rich-editor']}
manager={manager}
initialContent={state}
>
<Toolbar />
<EditorComponent />
<OnChangeJSON onChange={handleChange} />
{props.children}
</Remirror>
);
});
RichEditor.displayName = 'RichEditor';

@ -0,0 +1,16 @@
import {
BoldExtension,
CodeExtension,
ItalicExtension,
UnderlineExtension,
} from 'remirror/extensions';
/**
* 使
*/
export const extensions = () => [
new BoldExtension(),
new ItalicExtension(),
new UnderlineExtension(),
new CodeExtension(),
];

@ -0,0 +1,24 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { RichEditor } from './editor';
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
export default {
title: 'Tailchat/RichEditor',
component: RichEditor,
// More on argTypes: https://storybook.js.org/docs/react/api/argtypes
argTypes: {},
} as ComponentMeta<typeof RichEditor>;
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template: ComponentStory<typeof RichEditor> = (args) => (
<div style={{ height: 1000, width: '100%' }}>
<RichEditor {...args} />
</div>
);
export const Default = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args
Default.args = {
initContent: '<p>Hi <strong>Friend</strong></p>',
};

@ -0,0 +1,8 @@
import React from 'react';
/**
*
*/
export const RichEditor = React.lazy(() =>
import('./editor').then((module) => ({ default: module.RichEditor }))
);

@ -0,0 +1,26 @@
import React from 'react';
import {
FloatingToolbar,
CommandButtonGroup,
ToggleBoldButton,
ToggleItalicButton,
ToggleUnderlineButton,
ToggleCodeButton,
} from '@remirror/react';
/**
*
*/
export const Toolbar: React.FC = React.memo(() => {
return (
<FloatingToolbar>
<CommandButtonGroup>
<ToggleBoldButton />
<ToggleItalicButton />
<ToggleUnderlineButton />
<ToggleCodeButton />
</CommandButtonGroup>
</FloatingToolbar>
);
});
Toolbar.displayName = 'Toolbar';

@ -23,11 +23,14 @@
"homepage": "https://github.com/msgbyte/tailchat#readme",
"dependencies": {
"@iconify/react": "^3.2.1",
"@remirror/pm": "^2.0.0",
"@remirror/react": "^2.0.9",
"ahooks": "^3.7.1",
"antd": "^4.19.5",
"clsx": "^1.1.1",
"lodash": "^4.17.21",
"react-fastify-form": "1.0.10",
"remirror": "^2.0.9",
"str2int": "^1.1.0"
},
"devDependencies": {

@ -1,13 +1,11 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"target": "esnext",
"lib": ["DOM"],
"jsx": "react",
"esModuleInterop": true,
"isolatedModules": true,
"module": "ESNext",
"moduleResolution": "node",
"strict": true,
"importsNotUsedAsValues": "error",
"resolveJsonModule": true,
"typeRoots": ["./node_modules/@types", "../node_modules/@types", "./types"]

File diff suppressed because it is too large Load Diff

@ -1,10 +1,9 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"importsNotUsedAsValues": "error",
"jsx": "react",

@ -0,0 +1,9 @@
{
"compilerOptions": {
"esModuleInterop": true,
"isolatedModules": true,
"strict": true,
"importsNotUsedAsValues": "error",
"experimentalDecorators": true,
}
}
Loading…
Cancel
Save