From 738d23ca9d3a565cb40fcf594fcbdfbb16ecfc74 Mon Sep 17 00:00:00 2001 From: callmeyan Date: Fri, 4 Aug 2023 23:50:54 +0800 Subject: [PATCH] init --- .eslintrc.cjs | 14 + .gitignore | 24 + index.html | 15 + package.json | 50 + public/vite.svg | 1 + src/App.less | 193 + src/App.tsx | 20 + src/assets/arrow-right.svg | 6 + src/assets/react.svg | 5 + src/components/Editor/components.tsx | 29 + src/components/Editor/index.tsx | 5 + src/components/EditorComponents.tsx | 196 + src/components/SimpleSlateEditor.tsx | 161 + src/components/SlateEditor/Editor.tsx | 42 + .../FormatToolbar/FormatButton.tsx | 51 + .../FormatToolbar/FormatToolbar.tsx | 71 + src/components/SlateEditor/SlateSimple.tsx | 48 + src/components/icons/IconArrowRight.tsx | 15 + src/components/icons/IconCancelFill.tsx | 17 + src/components/icons/IconCheckFill.tsx | 17 + src/components/quill/QuillEditor.tsx | 98 + src/components/quill/ReplaceParser.tsx | 80 + src/components/quill/editor.ts | 44 + src/index.css | 69 + src/main.tsx | 9 + src/pages/quill/text-editor.less | 32 + src/pages/quill/text.tsx | 195 + .../wang/components/ProofreadPopover.tsx | 11 + src/pages/wang/plugins/index.ts | 16 + src/pages/wang/plugins/proofread.ts | 59 + src/pages/wang/style.less | 133 + src/pages/wang/wang-editor.tsx | 226 ++ src/types/editor.ts | 66 + src/vite-env.d.ts | 1 + tsconfig.json | 24 + tsconfig.node.json | 10 + vite.config.ts | 15 + yarn.lock | 3280 +++++++++++++++++ 38 files changed, 5348 insertions(+) create mode 100644 .eslintrc.cjs create mode 100644 .gitignore create mode 100644 index.html create mode 100644 package.json create mode 100644 public/vite.svg create mode 100644 src/App.less create mode 100644 src/App.tsx create mode 100644 src/assets/arrow-right.svg create mode 100644 src/assets/react.svg create mode 100644 src/components/Editor/components.tsx create mode 100644 src/components/Editor/index.tsx create mode 100644 src/components/EditorComponents.tsx create mode 100644 src/components/SimpleSlateEditor.tsx create mode 100644 src/components/SlateEditor/Editor.tsx create mode 100644 src/components/SlateEditor/FormatToolbar/FormatButton.tsx create mode 100644 src/components/SlateEditor/FormatToolbar/FormatToolbar.tsx create mode 100644 src/components/SlateEditor/SlateSimple.tsx create mode 100644 src/components/icons/IconArrowRight.tsx create mode 100644 src/components/icons/IconCancelFill.tsx create mode 100644 src/components/icons/IconCheckFill.tsx create mode 100644 src/components/quill/QuillEditor.tsx create mode 100644 src/components/quill/ReplaceParser.tsx create mode 100644 src/components/quill/editor.ts create mode 100644 src/index.css create mode 100644 src/main.tsx create mode 100644 src/pages/quill/text-editor.less create mode 100644 src/pages/quill/text.tsx create mode 100644 src/pages/wang/components/ProofreadPopover.tsx create mode 100644 src/pages/wang/plugins/index.ts create mode 100644 src/pages/wang/plugins/proofread.ts create mode 100644 src/pages/wang/style.less create mode 100644 src/pages/wang/wang-editor.tsx create mode 100644 src/types/editor.ts create mode 100644 src/vite-env.d.ts create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts create mode 100644 yarn.lock diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..4020bcb --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,14 @@ +module.exports = { + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': 'warn', + }, +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/index.html b/index.html new file mode 100644 index 0000000..3f80632 --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + + 协作-智能AI写作、文档纠错校对、文本图像合规检测平台 + + + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..bc2059c --- /dev/null +++ b/package.json @@ -0,0 +1,50 @@ +{ + "name": "editor", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/css": "^11.11.0", + "@fortawesome/free-solid-svg-icons": "^6.4.0", + "@fortawesome/react-fontawesome": "^0.2.0", + "@slate-yjs/core": "^1.0.0", + "@slate-yjs/react": "^1.0.0", + "@textbus/collaborate": "^3.3.3", + "@textbus/core": "^3.3.3", + "@textbus/platform-browser": "^3.3.3", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-react": "^1.0.6", + "ahooks": "^3.7.7", + "antd": "^5.5.1", + "classnames": "^2.3.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-quill": "^2.0.0", + "react-router-dom": "^6.11.2", + "slate": "^0.94.1", + "slate-react": "^0.94.2", + "yjs": "^13.6.1" + }, + "devDependencies": { + "@types/react": "^18.0.28", + "@types/react-dom": "^18.0.11", + "@typescript-eslint/eslint-plugin": "^5.57.1", + "@typescript-eslint/parser": "^5.57.1", + "@vitejs/plugin-react": "^4.0.0", + "eslint": "^8.38.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.3.4", + "less": "^4.1.3", + "typescript": "^5.0.2", + "vite": "^4.3.2" + }, + "peerDependencies": { + "snabbdom": "^3.5.1" + } +} diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/App.less b/src/App.less new file mode 100644 index 0000000..1e07f4e --- /dev/null +++ b/src/App.less @@ -0,0 +1,193 @@ +* { + box-sizing: border-box; +} + +#root { + margin: 0 auto; +} + +body { + margin: 0; + padding: 0; +} + +.svg-icon { + width: 18px; + height: 18px; +} + +.proofread-popover { + --antd-arrow-background-color: #666; + .ant-popover-inner{ + padding: 5px 10px; + background-color: var(--antd-arrow-background-color); + } + .ant-popover-inner-content{ + color:#fff; + } +} + +.page-example { +} + +.page-block-header { + +} + +.page-block-article { + --box-shadow: 0 0 5px rgba(0, 0, 0, 0.2); + --box-radius: 10px; + height: 100vh; + display: flex; + + .editor-container { + height: 100%; + display: flex; + flex-direction: column; + + .quill { + flex: 1; + } + } + + .ql-container.ql-snow { + border: none; + } + + #quill-editor-toolbar { + border: none; + box-shadow: var(--box-shadow); + margin: 20px; + border-radius: var(--box-radius); + } +} + +.page-block-content { + flex: 1; +} + +.page-block-control { + width: 400px; + padding: 20px; + + .replace-content-container { + box-shadow: var(--box-shadow); + border-radius: var(--box-radius); + height: 100%; + } +} + +.replace-content-container { + padding: 15px; + + .replace-item { + box-shadow: var(--box-shadow); + border-radius: 3px; + margin-bottom: 15px; + padding: 8px 10px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: space-between; + transition: all 0.3s; + outline: solid 2px transparent; + + .data { + display: flex; + align-items: center; + } + + &:last-child { + margin-bottom: 0; + } + + &:hover { + background-color: rgba(239, 243, 255, 0.83); + } + + &.replace-type-delete { + .origin { + text-decoration: line-through; + } + + .delete-text { + color: #f00; + margin-left: 3px; + } + } + + // state + &.replace-state-selected { + background-color: rgba(239, 243, 255, 0.83); + padding-bottom: 50px; + outline: solid 2px #747bff; + } + + .origin { + margin-left: 5px; + color: #f00; + max-width: 80px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .arrow { + margin: 0 8px; + } + + .icon-arrow { + width: 18px; + height: 18px; + display: block; + } + + .text { + font-weight: bold; + max-width: 80px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .action { + span { + font-size: 12px; + display: inline-block; + margin-left: 5px; + } + } + } +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} + +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +//减少动效媒体查询 +//https://www.cnblogs.com/xiaolantian/p/12701279.html +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..bb5a03c --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,20 @@ +// import {SimpleSlateEditor} from "./components/SimpleSlateEditor.tsx"; +import '@/App.less' +import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom"; +import TextPage from "./pages/quill/text.tsx"; +import WangEditor from "./pages/wang/wang-editor.tsx"; + + +function App() { + + return ( + + }/> + }/> + }/> + + + ) +} + +export default App diff --git a/src/assets/arrow-right.svg b/src/assets/arrow-right.svg new file mode 100644 index 0000000..0410e32 --- /dev/null +++ b/src/assets/arrow-right.svg @@ -0,0 +1,6 @@ + + + + diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..fb8f175 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1,5 @@ + diff --git a/src/components/Editor/components.tsx b/src/components/Editor/components.tsx new file mode 100644 index 0000000..2b825c6 --- /dev/null +++ b/src/components/Editor/components.tsx @@ -0,0 +1,29 @@ +import {ContentType, defineComponent, Injector} from "@textbus/core"; +import {ComponentLoader} from '@textbus/platform-browser' + +export const editorRootComponent = defineComponent({ + name: 'editorRootComponent', + type: ContentType.BlockComponent, + setup(_) { + return { + render() { + return ( +
+
这是我的第一个组件
+
+ ) + } + } + } + +}) + +export const editorRootComponentLoader: ComponentLoader = { + match(): boolean { + return true; + }, + read(_: HTMLElement, injector: Injector) { + return editorRootComponent.createInstance(injector); + } + +} diff --git a/src/components/Editor/index.tsx b/src/components/Editor/index.tsx new file mode 100644 index 0000000..b9bc572 --- /dev/null +++ b/src/components/Editor/index.tsx @@ -0,0 +1,5 @@ +import {Viewer} from "@textbus/platform-browser"; +import {editorRootComponent, editorRootComponentLoader} from "./components.tsx"; + + +export const Editor = new Viewer(editorRootComponent,editorRootComponentLoader); diff --git a/src/components/EditorComponents.tsx b/src/components/EditorComponents.tsx new file mode 100644 index 0000000..47d3dc1 --- /dev/null +++ b/src/components/EditorComponents.tsx @@ -0,0 +1,196 @@ +import React, { Ref, PropsWithChildren } from 'react' +import ReactDOM from 'react-dom' +import { cx, css } from '@emotion/css' + +interface BaseProps { + className: string + [key: string]: unknown +} +type OrNull = T | null + +export const Button = React.forwardRef( + ( + { + className, + active, + reversed, + ...props + }: PropsWithChildren< + { + active: boolean + reversed: boolean + } & BaseProps + >, + ref: Ref> + ) => ( + + ) +) + +export const EditorValue = React.forwardRef( + ( + { + className, + value, + ...props + }: PropsWithChildren< + { + value: any + } & BaseProps + >, + ref: Ref> + ) => { + const textLines = value.document.nodes + .map(node => node.text) + .toArray() + .join('\n') + return ( +
+
+ Slate's value as text +
+
+ {textLines} +
+
+ ) + } +) + +export const Icon = React.forwardRef( + ( + { className, ...props }: PropsWithChildren, + ref: Ref> + ) => ( + + ) +) + +export const Instruction = React.forwardRef( + ( + { className, ...props }: PropsWithChildren, + ref: Ref> + ) => ( +
+ ) +) + +export const Menu = React.forwardRef( + ( + { className, ...props }: PropsWithChildren, + ref: Ref> + ) => ( +
* { + display: inline-block; + } + + & > * + * { + margin-left: 15px; + } + ` + )} + /> + ) +) + +export const Portal = ({ children }) => { + return typeof document === 'object' + ? ReactDOM.createPortal(children, document.body) + : null +} + +export const Toolbar = React.forwardRef( + ( + { className, ...props }: PropsWithChildren, + ref: Ref> + ) => ( + + ) +) diff --git a/src/components/SimpleSlateEditor.tsx b/src/components/SimpleSlateEditor.tsx new file mode 100644 index 0000000..409a811 --- /dev/null +++ b/src/components/SimpleSlateEditor.tsx @@ -0,0 +1,161 @@ +import React, {useCallback, useMemo, useState} from "react"; +import {Slate, withReact, Editable} from "slate-react"; +import { + createEditor, Descendant, + Element as SlateElement +} from "slate"; +import {Toolbar} from "./EditorComponents.tsx"; +import {css} from "@emotion/css"; + +const initialValue: any[] = [ + { + type:'heading-one', + children:[ + {text:'111'} + ] + }, + { + type: 'paragraph', + children: [ + {text: 'This is editable '}, + {text: 'rich', bold: true}, + {text: ' text, '}, + {text: 'much', italic: true}, + {text: '测试一下', replace: true}, + {text: ' better than a '}, + {text: '