{"id":14220344,"url":"https://github.com/worktile/slate-angular","last_synced_at":"2025-04-05T00:06:20.394Z","repository":{"id":40251363,"uuid":"353215016","full_name":"worktile/slate-angular","owner":"worktile","description":"Angular view layer for Slate","archived":false,"fork":false,"pushed_at":"2024-05-28T03:35:27.000Z","size":3017,"stargazers_count":172,"open_issues_count":6,"forks_count":29,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-05-28T12:52:38.678Z","etag":null,"topics":["angular","rich-text-editor","slate","slate-angular"],"latest_commit_sha":null,"homepage":"http://slate-angular.ngnice.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/worktile.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-31T03:37:10.000Z","updated_at":"2024-05-30T04:58:34.871Z","dependencies_parsed_at":"2024-04-09T03:24:26.483Z","dependency_job_id":"9692f076-d3bb-4ae0-8358-ca14b1865b47","html_url":"https://github.com/worktile/slate-angular","commit_stats":{"total_commits":289,"total_committers":14,"mean_commits":"20.642857142857142","dds":0.301038062283737,"last_synced_commit":"b72e692defbde6c599e76870d4dfc75dc1c96104"},"previous_names":[],"tags_count":54,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/worktile%2Fslate-angular","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/worktile%2Fslate-angular/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/worktile%2Fslate-angular/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/worktile%2Fslate-angular/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/worktile","download_url":"https://codeload.github.com/worktile/slate-angular/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247266563,"owners_count":20910836,"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":["angular","rich-text-editor","slate","slate-angular"],"created_at":"2024-08-19T15:01:40.568Z","updated_at":"2025-04-05T00:06:20.379Z","avatar_url":"https://github.com/worktile.png","language":"TypeScript","readme":"# slate-angular\n\n[![CircleCI](https://circleci.com/gh/worktile/slate-angular.svg?style=shield)](https://circleci.com/gh/worktile/slate-angular)\n[![Coverage Status][coveralls-image]][coveralls-url]\n[![npm (scoped)](https://img.shields.io/npm/v/slate-angular?style=flat)](https://www.npmjs.com/package/slate-angular)\n[![npm](https://img.shields.io/npm/dm/slate-angular)](https://www.npmjs.com/package/slate-angular)\n![npm bundle size (scoped)](https://img.shields.io/bundlephobia/min/slate-angular)\n[![Telegram](https://img.shields.io/badge/-Telegram-red?style=social\u0026logo=telegram)](https://t.me/slateangular)\n\n[coveralls-image]: https://coveralls.io/repos/github/worktile/slate-angular/badge.svg?branch=master\n[coveralls-url]: https://coveralls.io/github/worktile/slate-angular\n\nAngular view layer for Slate\n\n[中文文档](https://github.com/worktile/slate-angular/blob/master/README.zh-CN.md)\n\n## Introduction\n\n[Slate](https://github.com/ianstormtaylor/slate) is a completely customizable framework for building rich text editors, including the model layer and view layer, but the slate only provides the view layer based on react, slate-angular is a supplement to the slate view layer, to help you use angular to build rich text editor.\n\nslate-angular is inspired by slate-react, and try to keep the style of slate and angular, friendly to Chinese input, start your slate-angular journey.\n\n## Demo\n\n[Try out our live demo](http://slate-angular.ngnice.com)\n\n![editor-preview.png](https://cdn.worktile.com/open-sources/slate-angular/editor-preview.gif)\n\n## Feature\n\n-   Support element front and rear cursor scheme\n-   Support custom component/template rendering Element\n-   Support custom component/template to render Text\n-   Support custom component/template rendering Leaf\n-   Support decorate decoration\n-   Support void element\n\n### Compatible browser\n\nChrome、Edge、Safari、Firefox、QQ Browser\n\n## Usage\n\n### 1. Install dependencies\n\n```\n\"dependencies\": {\n    \"direction\": \"^2.0.1\",\n    \"is-hotkey\": \"^0.2.0\",\n    \"slate\": \"~0.101.5\",\n    \"slate-history\": \"~0.100.0\",\n    \"slate-angular\": \"~16.1.0-next.8\"\n}\n```\n\n### 2. Loading SlateModule in AppModule\n\n```\nimport { FormsModule } from '@angular/forms';\nimport { SlateModule } from 'slate-angular';\n\n@NgModule({\n  imports: [\n    // ...,\n    FormsModule,\n    SlateModule\n  ],\n  // ...\n})\nexport class AppModule { }\n```\n\n### 3. Import index.scss\n\nsrc/styles.scss\n\n```\n@use 'slate-angular/styles/index.scss';\n\n// basic richtext styles\n.slate-editable-container {\n    [slate-underlined][slate-strike] {\n        text-decoration: underline line-through;\n    }\n    [slate-strike] {\n        text-decoration: line-through;\n    }\n    [slate-underlined] {\n        text-decoration: underline;\n    }\n    [slate-italic] {\n        font-style: italic;\n    }\n    [slate-bold] {\n        font-weight: bold;\n    }\n    [slate-code-line] {\n        margin: 0 4px;\n        padding: 2px 3px;\n        border: 1px solid rgba($color: #000000, $alpha: 0.08);\n        border-radius: 2px;\n        background-color: rgba($color: #000000, $alpha: 0.06);\n    }\n\n    blockquote {\n        margin: 0;\n        margin-left: 0;\n        margin-right: 0;\n        color: #888;\n        padding-left: 10px !important;\n        border-left: 4px solid #eee;\n    }\n\n    h1,h2,h3 {\n        margin: 0px;\n    }\n\n    \u0026\u003e[data-slate-node=\"element\"],\u0026\u003eslate-block-card {\n        margin-bottom: 12px;\n    }\n}\n\n// basic richtext container styles\n.demo-richtext-container {\n    max-width: 42em;\n    margin: 50px auto;\n    background-color: #fff;\n    box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.2);\n}\n```\n\n### 4. Add text-mark component\n\n```\nimport { ChangeDetectorRef, Component, ElementRef, Renderer2 } from \"@angular/core\";\nimport { BaseTextComponent } from \"slate-angular\";\n\nexport enum MarkTypes {\n    bold = 'bold',\n    italic = 'italic',\n    underline = 'underlined',\n    strike = 'strike',\n    code = 'code-line'\n}\n\n@Component({\n    selector: 'span[textMark]',\n    template: ``,\n    host: {\n        'data-slate-node': 'text'\n    }\n})\nexport class DemoTextMarkComponent extends BaseTextComponent {\n    attributes: string[] = [];\n\n    constructor(public renderer2: Renderer2) {\n        super();\n    }\n\n    applyTextMark() {\n        this.attributes.forEach(attr =\u003e {\n            this.renderer2.removeAttribute(this.elementRef.nativeElement, attr);\n        });\n        this.attributes = [];\n        for (const key in this.text) {\n            if (Object.prototype.hasOwnProperty.call(this.text, key) \u0026\u0026 key !== 'text') {\n                const attr = `slate-${key}`;\n                this.renderer2.setAttribute(this.elementRef.nativeElement, attr, 'true');\n                this.attributes.push(attr);\n            }\n        }\n    }\n\n    onContextChange() {\n        super.onContextChange();\n        this.applyTextMark();\n    }\n}\n```\n\n### 5. Use slate-editable component\n\n**Template**\n\n```\n\u003cdiv class=\"demo-richtext-container\"\u003e\n    \u003cslate-editable [editor]=\"editor\" [(ngModel)]=\"value\"\n        (ngModelChange)=\"valueChange($event)\"\n        [renderElement]=\"renderElement\"\n        [renderText]=\"renderText\"\u003e\n        \u003cng-template #heading_1 let-context=\"context\" let-viewContext=\"viewContext\"\u003e\n            \u003ch1 slateElement [context]=\"context\" [viewContext]=\"viewContext\"\u003e\u003c/h1\u003e\n        \u003c/ng-template\u003e\n        \u003cng-template #heading_2 let-context=\"context\" let-viewContext=\"viewContext\"\u003e\n            \u003ch2 slateElement [context]=\"context\" [viewContext]=\"viewContext\"\u003e\u003c/h2\u003e\n        \u003c/ng-template\u003e\n        \u003cng-template #heading_3 let-context=\"context\" let-viewContext=\"viewContext\"\u003e\n            \u003ch3 slateElement [context]=\"context\" [viewContext]=\"viewContext\"\u003e\u003c/h3\u003e\n        \u003c/ng-template\u003e\n        \u003cng-template #blockquote let-context=\"context\" let-viewContext=\"viewContext\"\u003e\n            \u003cblockquote slateElement [context]=\"context\" [viewContext]=\"viewContext\"\u003e\u003c/blockquote\u003e\n        \u003c/ng-template\u003e\n        \u003cng-template #ul let-context=\"context\" let-viewContext=\"viewContext\"\u003e\n            \u003cul slateElement [context]=\"context\" [viewContext]=\"viewContext\"\u003e\u003c/ul\u003e\n        \u003c/ng-template\u003e\n        \u003cng-template #ol let-context=\"context\" let-viewContext=\"viewContext\"\u003e\n            \u003col slateElement [context]=\"context\" [viewContext]=\"viewContext\"\u003e\u003c/ol\u003e\n        \u003c/ng-template\u003e\n        \u003cng-template #li let-context=\"context\" let-viewContext=\"viewContext\"\u003e\n            \u003cli slateElement [context]=\"context\" [viewContext]=\"viewContext\"\u003e\u003c/li\u003e\n        \u003c/ng-template\u003e\n    \u003c/slate-editable\u003e\n\u003c/div\u003e\n```\n\n**TS**\n\n```\nimport { Component, ViewChild, TemplateRef } from '@angular/core';\nimport { createEditor, Element } from 'slate';\nimport { withHistory } from 'slate-history';\nimport { withAngular } from 'slate-angular';\nimport { DemoTextMarkComponent, MarkTypes } from './text-mark.component';\n\n@Component({\n  selector: 'app-root',\n  templateUrl: './app.component.html',\n  styleUrls: ['./app.component.scss']\n})\nexport class AppComponent {\n  \ttitle = 'slate-angular-basic';\n    value = initialValue;\n\n    @ViewChild('heading_1', { read: TemplateRef, static: true })\n    headingOneTemplate!: TemplateRef\u003cany\u003e;\n\n    @ViewChild('heading_2', { read: TemplateRef, static: true })\n    headingTwoTemplate!: TemplateRef\u003cany\u003e;\n\n    @ViewChild('heading_3', { read: TemplateRef, static: true })\n    headingThreeTemplate!: TemplateRef\u003cany\u003e;\n\n    @ViewChild('blockquote', { read: TemplateRef, static: true })\n    blockquoteTemplate!: TemplateRef\u003cany\u003e;\n\n    @ViewChild('ul', { read: TemplateRef, static: true })\n    ulTemplate!: TemplateRef\u003cany\u003e;\n\n    @ViewChild('ol', { read: TemplateRef, static: true })\n    olTemplate!: TemplateRef\u003cany\u003e;\n\n    @ViewChild('li', { read: TemplateRef, static: true })\n    liTemplate!: TemplateRef\u003cany\u003e;\n\n    editor = withHistory(withAngular(createEditor()));\n\n    ngOnInit(): void {\n    }\n\n    valueChange(value: Element[]) {\n    }\n\n    renderElement = (element: any) =\u003e {\n        if (element.type === 'heading-one') {\n            return this.headingOneTemplate;\n        }\n        if (element.type === 'heading-two') {\n            return this.headingTwoTemplate;\n        }\n        if (element.type === 'heading-three') {\n            return this.headingThreeTemplate;\n        }\n        if (element.type === 'block-quote') {\n            return this.blockquoteTemplate;\n        }\n        if (element.type === 'numbered-list') {\n            return this.olTemplate;\n        }\n        if (element.type === 'bulleted-list') {\n            return this.ulTemplate;\n        }\n        if (element.type === 'list-item') {\n            return this.liTemplate;\n        }\n        return null;\n    }\n\n    renderText = (text: any) =\u003e {\n        if (text[MarkTypes.bold] || text[MarkTypes.italic] || text[MarkTypes.code] || text[MarkTypes.underline]) {\n            return DemoTextMarkComponent;\n        }\n      \treturn null;\n    }\n}\n\nconst initialValue = [\n    {\n        type: 'paragraph',\n        children: [\n            { text: 'This is editable ' },\n            { text: 'rich', bold: true },\n            { text: ' text, ' },\n            { text: 'much', bold: true, italic: true },\n            { text: ' better than a ' },\n            { text: '\u003ctextarea\u003e', 'code-line': true },\n            { text: '!' }\n        ]\n    },\n    {\n        type: 'heading-one',\n        children: [{ text: 'This is h1 ' }]\n    },\n    {\n        type: 'heading-three',\n        children: [{ text: 'This is h3 ' }]\n    },\n    {\n        type: 'paragraph',\n        children: [\n            {\n                text: `Since it's rich text, you can do things like turn a selection of text `\n            },\n            { text: 'bold', bold: true },\n            {\n                text: ', or add a semantically rendered block quote in the middle of the page, like this:'\n            }\n        ]\n    },\n    {\n        type: 'block-quote',\n        children: [{ text: 'A wise quote.' }]\n    },\n    {\n        type: 'paragraph',\n        children: [{ text: 'Try it out for yourself!' }]\n    },\n    {\n        type: 'paragraph',\n        children: [{ text: '' }]\n    }\n];\n\n```\n\n### 6. Startup basic demo\n\n\u003e Before starting, you need to declare the DemoTextMarkComponent component in NgModule\n\n\u003e You can checkout a [stackblitz implementation of the readme usage](https://stackblitz.com/edit/angular-ivy-pqofah?file=src/app/app.component.ts)\n\n**Start the demo and you will get the following interface**\n\n![image.png](https://atlas-rc.pingcode.com/files/public/61ac3e16e429e861587f55db)\n\n\u003e Currently, there is no toolbar. You need to add toolbars and processing functions according to your own icon library.\n\nbasic usage: [https://github.com/pubuzhixing8/slate-angular-basic](https://github.com/pubuzhixing8/slate-angular-basic)\n\n## Who is using slate-angular?\n\n\u003ctable style=\"margin-top: 20px;\"\u003e\n  \u003ctr\u003e\n    \u003ctd width=\"160\" align=\"center\" style=\"padding: 20px\"\u003e\n      \u003ca target=\"_blank\" href=\"https://pingcode.com/product/wiki?utm_source=github-slate-angular\"\u003e\n        \u003cimg src=\"https://cdn.pingcode.com/static/portal/assets/app-icons/app-wiki-square-fill-large.svg\" height=\"40\" /\u003e\n        \u003cbr /\u003e\n        \u003cstrong\u003ePingCode Wiki\u003c/strong\u003e\n      \u003c/a\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n## 💻 Development\n\n```bash\nnpm install   // Installs package dependencies\n```\n\n```bash\nnpm run start              // run demo\nnpm run build              // build new slate-angular\n\nnpm run test               // run unit tests\n```\n\n### Prerequisites\n\nAngular \u003e= 10.\\*\n\nSlate \u003e= 0.63.0\n\n## Contributing\n\n🌟 Stars and 📥 Pull requests to worktile/slate-angular are welcome!\n\n## LICENSE\n\n[MIT License](https://github.com/worktile/slate-angular/blob/master/LICENSE)\n","funding_links":[],"categories":["Third Party Components"],"sub_categories":["Editors"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fworktile%2Fslate-angular","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fworktile%2Fslate-angular","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fworktile%2Fslate-angular/lists"}