{"id":23394505,"url":"https://github.com/kanweiwei/easy-editor","last_synced_at":"2025-04-11T12:53:50.954Z","repository":{"id":39083281,"uuid":"272027506","full_name":"kanweiwei/easy-editor","owner":"kanweiwei","description":"基于slate框架的富文本编辑器","archived":false,"fork":false,"pushed_at":"2024-03-05T11:21:04.000Z","size":4888,"stargazers_count":40,"open_issues_count":23,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-25T09:11:57.140Z","etag":null,"topics":["editor","react","rich-text-editor","slate"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kanweiwei.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-06-13T14:45:08.000Z","updated_at":"2024-10-18T14:14:33.000Z","dependencies_parsed_at":"2023-02-05T19:31:18.294Z","dependency_job_id":null,"html_url":"https://github.com/kanweiwei/easy-editor","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanweiwei%2Feasy-editor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanweiwei%2Feasy-editor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanweiwei%2Feasy-editor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanweiwei%2Feasy-editor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kanweiwei","download_url":"https://codeload.github.com/kanweiwei/easy-editor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248403753,"owners_count":21097585,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["editor","react","rich-text-editor","slate"],"created_at":"2024-12-22T06:16:03.176Z","updated_at":"2025-04-11T12:53:50.935Z","avatar_url":"https://github.com/kanweiwei.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eEasy Editor\u003c/h1\u003e\n\u003cdiv align=\"center\"\u003e\n\u003cimg src=\"https://img.shields.io/npm/v/@camol/easy-editor?style=flat-square\"/\u003e\n\u003cimg alt=\"GitHub\" src=\"https://img.shields.io/github/license/kanweiwei/easy-editor\"\u003e\n\u003c/div\u003e\n\u003cdiv align=\"center\"\u003eA rich text editor based on slate \u003c/div\u003e\n\n![easy-editor](./source/images/easy-editor-demo.gif)\n\n[Demo](https://kanweiwei.github.io/easy-editor/example/)\n\n## Quick Start\n\n### npm\n\n```bash\nnpm install @camol/easy-editor --save\n```\n\n### yarn\n\n```bash\nyarn add @camol/easy-editor -S\n```\n\n### Examples\n\n```bash\ngit clone https://github.com/kanweiwei/easy-editor.git\ncd easy-editor\nyarn \u0026\u0026 yarn build \u0026\u0026 yarn example\n```\n\n### with React\n\n```typescript\nimport * as React from \"react\";\nimport ReactDom from \"react-dom\";\nimport EasyEditor from \"@camol/easy-editor\";\n\nclass Editor extends React.Component {\n  html = \"\";\n\n  handleChange = (v: any) =\u003e {\n    console.log(\"change=\u003e\u003e\u003e\", v);\n    if (this.editorRef.current) {\n      // value to html\n      console.log(this.editorRef.current.convertor.serialize(v.change.value));\n    }\n  };\n\n  render() {\n    return \u003cEasyEditor value={\"\u003cp\u003e123\u003c/p\u003e\"} onChange={this.handleChange} /\u003e;\n  }\n}\n\nReactDom.render(\u003cEditor /\u003e, document.getElementById(\"root\"));\n```\n\n### Custom toolbar\n\nFor convenience, some tools have been built in the toolbar.\ntool| instructions|\n-|:-:|\nfontSize| 字号|\nfontColor| 字体颜色|\nbold| 加粗|\nitalic| 斜体|\nu| 下划线|\nsup| 上标 |\nsub | 下标 |\nleft|文字居左|\nright|文字居右|\ncenter|文字居中|\njustify|两端对齐|\nimage| 插入图片|\nvideo | 插入音频（mp4、webm）|\npdf | 插入 pdf|\nmath | 插入 latex 数学公式 |\n\n#### How to add a custom tool ?\n\n```typescript\nimport * as React from \"react\";\nimport ReactDom from \"react-dom\";\nimport EasyEditor from \"@camol/easy-editor\";\n\n// audio tool\nclass AudioControl extends React.Component {\n  inputRef = React.createRef();\n\n  handleClick = () =\u003e {\n    if (inputRef.current) {\n      inputRef.current.click();\n    }\n  };\n\n  handleChange = (e: React.ChangeEvent\u003cHTMLInputElement\u003e) =\u003e {\n    const file = e.target.files[0];\n    e.target.value = \"\";\n    if (file) {\n      if (this.props.beforeUpload) {\n        let url = await this.props.beforeUpload(file);\n        if (url) {\n          let change = this.props.change.focus().insertInline({\n            object: \"inline\",\n            type: \"audio\",\n            isVoid: true,\n            data: {\n              src: url,\n            },\n          });\n          this.props.update(change);\n        }\n      }\n    }\n  };\n\n  render() {\n    return (\n      \u003cspan onMouseDown={this.handleClick}\u003e\n        \u003cspan className=\"tool-insert-video\" /\u003e\n        \u003cinput\n          type=\"file\"\n          style={{ width: 0, height: 0, opacity: 0 }}\n          ref={this.inputRef}\n          onChange={this.handleChange}\n        /\u003e\n      \u003c/span\u003e\n    );\n  }\n}\n\nclass Editor extends React.Component {\n  html = \"\";\n\n  handleChange = (v: any) =\u003e {\n    console.log(\"change=\u003e\u003e\u003e\", v);\n    if (this.editorRef.current) {\n      // value to html\n      console.log(this.editorRef.current.convertor.serialize(v.change.value));\n    }\n  };\n\n  render() {\n    return (\n      \u003cEasyEditor\n        value={\"\u003cp\u003e123\u003c/p\u003e\"}\n        onChange={this.handleChange}\n        controls={[\n          [\"bold\", \"u\", \"image\"],\n          [\n            {\n              type: \"audio\",\n              component: AudioControl,\n            },\n          ],\n        ]}\n      /\u003e\n    );\n  }\n}\n```\n\n---\n\n### Custom Node Plugin\n\n```typescript\nimport * as React from \"react\";\nimport { AST } from \"parse5\";\n\nconst plugin = {\n  type: \"node\", // node, mark\n  object: \"inline\",\n  nodeType: \"super-audio\",\n  importer(el: AST.Default.Element, next: Function): any {\n    if (el.tagName.toLowerCase() === \"audio\") {\n      return {\n        object: \"inline\", // block、inline,\n        type: \"super-audio\",\n        isVoid: true,\n        data: {\n          src: el?.attrs?.find((n) =\u003e n.name === \"src\")?.value,\n        },\n        nodes: next(el.childNodes),\n      };\n    }\n  },\n  exporter(node: any, children: any): any {\n    let { className, src } = node.data.toJS();\n    return \u003caudio src={src} className={className}\u003e\u003c/audio\u003e;\n  },\n  render(\n    editor: any,\n    props: { attributes: any; children: any; node: any; isSelected: any }\n  ): any {\n    // @ts-ignore\n    const { attributes, children, node, isSelected } = props;\n    const src = node.data.get(\"src\");\n    return (\n      \u003cspan {...attributes}\u003e\n        \u003caudio src={src} controls\u003e\n          {children}\n        \u003c/audio\u003e\n      \u003c/span\u003e\n    );\n  },\n};\n\nexport default plugin;\n```\n\n---\n\n### License\n\nApache 2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkanweiwei%2Feasy-editor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkanweiwei%2Feasy-editor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkanweiwei%2Feasy-editor/lists"}