ProductPromotion
Logo

Angular.JS

made by https://0x3d.site

GitHub - worktile/slate-angular: Angular view layer for Slate
Angular view layer for Slate. Contribute to worktile/slate-angular development by creating an account on GitHub.
Visit Site

GitHub - worktile/slate-angular: Angular view layer for Slate

GitHub - worktile/slate-angular: Angular view layer for Slate

slate-angular

CircleCI Coverage Status npm (scoped) npm npm bundle size (scoped) Telegram

Angular view layer for Slate

中文文档

Introduction

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.

slate-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.

Demo

Try out our live demo

editor-preview.png

Feature

  • Support element front and rear cursor scheme
  • Support custom component/template rendering Element
  • Support custom component/template to render Text
  • Support custom component/template rendering Leaf
  • Support decorate decoration
  • Support void element

Compatible browser

Chrome、Edge、Safari、Firefox、QQ Browser

Usage

1. Install dependencies

"dependencies": {
    "direction": "^2.0.1",
    "is-hotkey": "^0.2.0",
    "slate": "~0.101.5",
    "slate-history": "~0.100.0",
    "slate-angular": "~16.1.0-next.8"
}

2. Loading SlateModule in AppModule

import { FormsModule } from '@angular/forms';
import { SlateModule } from 'slate-angular';

@NgModule({
  imports: [
    // ...,
    FormsModule,
    SlateModule
  ],
  // ...
})
export class AppModule { }

3. Import index.scss

src/styles.scss

@use 'slate-angular/styles/index.scss';

// basic richtext styles
.slate-editable-container {
    [slate-underlined][slate-strike] {
        text-decoration: underline line-through;
    }
    [slate-strike] {
        text-decoration: line-through;
    }
    [slate-underlined] {
        text-decoration: underline;
    }
    [slate-italic] {
        font-style: italic;
    }
    [slate-bold] {
        font-weight: bold;
    }
    [slate-code-line] {
        margin: 0 4px;
        padding: 2px 3px;
        border: 1px solid rgba($color: #000000, $alpha: 0.08);
        border-radius: 2px;
        background-color: rgba($color: #000000, $alpha: 0.06);
    }

    blockquote {
        margin: 0;
        margin-left: 0;
        margin-right: 0;
        color: #888;
        padding-left: 10px !important;
        border-left: 4px solid #eee;
    }

    h1,h2,h3 {
        margin: 0px;
    }

    &>[data-slate-node="element"],&>slate-block-card {
        margin-bottom: 12px;
    }
}

// basic richtext container styles
.demo-richtext-container {
    max-width: 42em;
    margin: 50px auto;
    background-color: #fff;
    box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.2);
}

4. Add text-mark component

import { ChangeDetectorRef, Component, ElementRef, Renderer2 } from "@angular/core";
import { BaseTextComponent } from "slate-angular";

export enum MarkTypes {
    bold = 'bold',
    italic = 'italic',
    underline = 'underlined',
    strike = 'strike',
    code = 'code-line'
}

@Component({
    selector: 'span[textMark]',
    template: ``,
    host: {
        'data-slate-node': 'text'
    }
})
export class DemoTextMarkComponent extends BaseTextComponent {
    attributes: string[] = [];

    constructor(public renderer2: Renderer2) {
        super();
    }

    applyTextMark() {
        this.attributes.forEach(attr => {
            this.renderer2.removeAttribute(this.elementRef.nativeElement, attr);
        });
        this.attributes = [];
        for (const key in this.text) {
            if (Object.prototype.hasOwnProperty.call(this.text, key) && key !== 'text') {
                const attr = `slate-${key}`;
                this.renderer2.setAttribute(this.elementRef.nativeElement, attr, 'true');
                this.attributes.push(attr);
            }
        }
    }

    onContextChange() {
        super.onContextChange();
        this.applyTextMark();
    }
}

5. Use slate-editable component

Template

<div class="demo-richtext-container">
    <slate-editable [editor]="editor" [(ngModel)]="value"
        (ngModelChange)="valueChange($event)"
        [renderElement]="renderElement"
        [renderText]="renderText">
        <ng-template #heading_1 let-context="context" let-viewContext="viewContext">
            <h1 slateElement [context]="context" [viewContext]="viewContext"></h1>
        </ng-template>
        <ng-template #heading_2 let-context="context" let-viewContext="viewContext">
            <h2 slateElement [context]="context" [viewContext]="viewContext"></h2>
        </ng-template>
        <ng-template #heading_3 let-context="context" let-viewContext="viewContext">
            <h3 slateElement [context]="context" [viewContext]="viewContext"></h3>
        </ng-template>
        <ng-template #blockquote let-context="context" let-viewContext="viewContext">
            <blockquote slateElement [context]="context" [viewContext]="viewContext"></blockquote>
        </ng-template>
        <ng-template #ul let-context="context" let-viewContext="viewContext">
            <ul slateElement [context]="context" [viewContext]="viewContext"></ul>
        </ng-template>
        <ng-template #ol let-context="context" let-viewContext="viewContext">
            <ol slateElement [context]="context" [viewContext]="viewContext"></ol>
        </ng-template>
        <ng-template #li let-context="context" let-viewContext="viewContext">
            <li slateElement [context]="context" [viewContext]="viewContext"></li>
        </ng-template>
    </slate-editable>
</div>

TS

import { Component, ViewChild, TemplateRef } from '@angular/core';
import { createEditor, Element } from 'slate';
import { withHistory } from 'slate-history';
import { withAngular } from 'slate-angular';
import { DemoTextMarkComponent, MarkTypes } from './text-mark.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  	title = 'slate-angular-basic';
    value = initialValue;

    @ViewChild('heading_1', { read: TemplateRef, static: true })
    headingOneTemplate!: TemplateRef<any>;

    @ViewChild('heading_2', { read: TemplateRef, static: true })
    headingTwoTemplate!: TemplateRef<any>;

    @ViewChild('heading_3', { read: TemplateRef, static: true })
    headingThreeTemplate!: TemplateRef<any>;

    @ViewChild('blockquote', { read: TemplateRef, static: true })
    blockquoteTemplate!: TemplateRef<any>;

    @ViewChild('ul', { read: TemplateRef, static: true })
    ulTemplate!: TemplateRef<any>;

    @ViewChild('ol', { read: TemplateRef, static: true })
    olTemplate!: TemplateRef<any>;

    @ViewChild('li', { read: TemplateRef, static: true })
    liTemplate!: TemplateRef<any>;

    editor = withHistory(withAngular(createEditor()));

    ngOnInit(): void {
    }

    valueChange(value: Element[]) {
    }

    renderElement = (element: any) => {
        if (element.type === 'heading-one') {
            return this.headingOneTemplate;
        }
        if (element.type === 'heading-two') {
            return this.headingTwoTemplate;
        }
        if (element.type === 'heading-three') {
            return this.headingThreeTemplate;
        }
        if (element.type === 'block-quote') {
            return this.blockquoteTemplate;
        }
        if (element.type === 'numbered-list') {
            return this.olTemplate;
        }
        if (element.type === 'bulleted-list') {
            return this.ulTemplate;
        }
        if (element.type === 'list-item') {
            return this.liTemplate;
        }
        return null;
    }

    renderText = (text: any) => {
        if (text[MarkTypes.bold] || text[MarkTypes.italic] || text[MarkTypes.code] || text[MarkTypes.underline]) {
            return DemoTextMarkComponent;
        }
      	return null;
    }
}

const initialValue = [
    {
        type: 'paragraph',
        children: [
            { text: 'This is editable ' },
            { text: 'rich', bold: true },
            { text: ' text, ' },
            { text: 'much', bold: true, italic: true },
            { text: ' better than a ' },
            { text: '<textarea>', 'code-line': true },
            { text: '!' }
        ]
    },
    {
        type: 'heading-one',
        children: [{ text: 'This is h1 ' }]
    },
    {
        type: 'heading-three',
        children: [{ text: 'This is h3 ' }]
    },
    {
        type: 'paragraph',
        children: [
            {
                text: `Since it's rich text, you can do things like turn a selection of text `
            },
            { text: 'bold', bold: true },
            {
                text: ', or add a semantically rendered block quote in the middle of the page, like this:'
            }
        ]
    },
    {
        type: 'block-quote',
        children: [{ text: 'A wise quote.' }]
    },
    {
        type: 'paragraph',
        children: [{ text: 'Try it out for yourself!' }]
    },
    {
        type: 'paragraph',
        children: [{ text: '' }]
    }
];

6. Startup basic demo

Before starting, you need to declare the DemoTextMarkComponent component in NgModule

You can checkout a stackblitz implementation of the readme usage

Start the demo and you will get the following interface

image.png

Currently, there is no toolbar. You need to add toolbars and processing functions according to your own icon library.

basic usage: https://github.com/pubuzhixing8/slate-angular-basic

Who is using slate-angular?

💻 Development

npm install   // Installs package dependencies
npm run start              // run demo
npm run build              // build new slate-angular

npm run test               // run unit tests

Prerequisites

Angular >= 10.*

Slate >= 0.63.0

Contributing

🌟 Stars and 📥 Pull requests to worktile/slate-angular are welcome!

LICENSE

MIT License

More Resources
to explore the angular.

mail [email protected] to add your project or resources here 🔥.

Related Articles
to learn about angular.

FAQ's
to learn more about Angular JS.

mail [email protected] to add more queries here 🔍.

More Sites
to check out once you're finished browsing here.

0x3d
https://www.0x3d.site/
0x3d is designed for aggregating information.
NodeJS
https://nodejs.0x3d.site/
NodeJS Online Directory
Cross Platform
https://cross-platform.0x3d.site/
Cross Platform Online Directory
Open Source
https://open-source.0x3d.site/
Open Source Online Directory
Analytics
https://analytics.0x3d.site/
Analytics Online Directory
JavaScript
https://javascript.0x3d.site/
JavaScript Online Directory
GoLang
https://golang.0x3d.site/
GoLang Online Directory
Python
https://python.0x3d.site/
Python Online Directory
Swift
https://swift.0x3d.site/
Swift Online Directory
Rust
https://rust.0x3d.site/
Rust Online Directory
Scala
https://scala.0x3d.site/
Scala Online Directory
Ruby
https://ruby.0x3d.site/
Ruby Online Directory
Clojure
https://clojure.0x3d.site/
Clojure Online Directory
Elixir
https://elixir.0x3d.site/
Elixir Online Directory
Elm
https://elm.0x3d.site/
Elm Online Directory
Lua
https://lua.0x3d.site/
Lua Online Directory
C Programming
https://c-programming.0x3d.site/
C Programming Online Directory
C++ Programming
https://cpp-programming.0x3d.site/
C++ Programming Online Directory
R Programming
https://r-programming.0x3d.site/
R Programming Online Directory
Perl
https://perl.0x3d.site/
Perl Online Directory
Java
https://java.0x3d.site/
Java Online Directory
Kotlin
https://kotlin.0x3d.site/
Kotlin Online Directory
PHP
https://php.0x3d.site/
PHP Online Directory
React JS
https://react.0x3d.site/
React JS Online Directory
Angular
https://angular.0x3d.site/
Angular JS Online Directory