Rich Editor

Inspired by making quadrats(aka artibox) currently based on slate

w/ quadrats v0

Core

How to define, query and transform json

How to define location

How to cooperate json w/ location

Text

interface Text {
  text: string;
}
const someText = {
  text: 'a text',
};

Marks

interface Text {
  text: string;
  [prop: string]: any;
}
const boldText = {
  text: 'bold text',
  bold: true,
};

The properties of text.

Element

Block, Inline, Void

interface Element {
  type: string;
  children: Node[];
  [prop: string]: any;
}
const paragraphElement = {
  type: 'paragraph',
  children: [...],
};

const linkElement = {
  type: 'link',
  children: [...],
  url: 'https://example.com',
};

Node

A node schema of json tree

type Node = Element | Text;

Editor

The root node of json tree.  Has other information e.g. selection.

interface Editor {
  children: Node[];
  selection?: Range;
}

Editor & Plugin(with)

controller

Location

Path

type Path = number[]
const editor = {
  children: [
    {
      type: 'paragraph',
      children: [
        {
          text: 'A line of text!',
        },
      ],
    },
  ],
}

The path of text is [0, 0].

Location

Point

interface Point {
  path: Path;
  offset: number;
}
const start = {
  path: [0, 0],
  offset: 0,
};

const end = {
  path: [0, 0],
  offset: 15,
};

Location

Range / Selection

interface Range {
  anchor: Point;
  focus: Point;
}

Location

Range / Selection - Collapsed

anchor === focus

Location

Range / Selection - Expanded(forwards)

anchor is before focus

Location

Range / Selection - Expanded(backwards)

anchor is after focus

Query

Transform

Normalizing

Currently issues slate has

Not Tree Shakable Api

export const Transforms = {
  insertNodes() {
  },
  wrapNodes() {
  },
};
export function insertNodes() {
}

export function wrapNodes() {
}

Currently issues slate has

Too low level apis

Some common queries or transforms need to implemented by ourselves

Currently issues slate has

...etc

Common

All common features a rich editor will have.

Common

Provide all a feature needing
utils/helpers
queries
transforms

...etc

E.g. Blockquote

Platform

The libary/framework to render json and handle events

E.g. React

editor related components
event handlers
hooks
...etc.

React

Rendering

React

event handlers

React - Common

blockquote
extends core
provide renderer and event handlers

React - Widgets

toolbar

React - Common

blockquote - toolbar

React - Serializers

jsx-serializer

Currently issues slate has

Not really care about composition event.
Currently I just hack.

Currently issues slate has

...etc

Usage

Structure

e.g. react

core

common/a

common/b

react/a

react/b

react/a/toolbar

react/b/toolbar

react

react/toolbar

Testing

w/ jsx

Use Jsx w/o React

/** @jsx jsx */

import { jsx } from 'somewhere';
/** @jsx jsx */

import { jsx } from 'somewhere';

const input = (
  <editor>
    <element>
      <cursor />
      foo
    </element>
  </editor>
);

const output = {
  children: [
    {
      children: [
        {
          text: 'foo'
        }
      ]
    }
  ],
  selection: {
    anchor: { path: [0, 0], offset: 0 },
    focus: { path: [0, 0], offset: 0 },
  }
};

Write Json w/ Jsx

/** @jsx jsx */

import { jsx } from '../../../../__fixtures__/hyperscript';
import { isSelectionAtBlockEdge } from './isSelectionAtBlockEdge';

describe('isSelectionAtBlockEdge', () => {
  it('collpased on block start', () => {
    const input = (
      <editor>
        <element>
          <cursor />
          foo
        </element>
      </editor>
    );

    expect(isSelectionAtBlockEdge(input)).toBe('start');
  });

  it('collpased on block center', () => {
    const input = (
      <editor>
        <element>
          fo
          <cursor />
          o
        </element>
      </editor>
    );

    expect(isSelectionAtBlockEdge(input)).toBe(undefined);
  });

  it('collpased on block end', () => {
    const input = (
      <editor>
        <element>
          foo
          <cursor />
        </element>
      </editor>
    );

    expect(isSelectionAtBlockEdge(input)).toBe('end');
  });

  it('expanded, focus on block start', () => {
    const input = (
      <editor>
        <element>
          <focus />
          fo
          <anchor />
          o
        </element>
      </editor>
    );

    expect(isSelectionAtBlockEdge(input)).toBe('start');
  });

  it('expanded, focus on block center', () => {
    const input = (
      <editor>
        <element>
          <anchor />
          fo
          <focus />
          o
        </element>
      </editor>
    );

    expect(isSelectionAtBlockEdge(input)).toBe(undefined);
  });

  it('expanded, focus on block end', () => {
    const input = (
      <editor>
        <element>
          fo
          <anchor />
          o
          <focus />
        </element>
      </editor>
    );

    expect(isSelectionAtBlockEdge(input)).toBe('end');
  });
});

Roadmap

Rewrite core

remove slate
make apis friendly
provide all common apis
well testing
PR welcome

Testing common

PR welcome

Rewrite react

remove slate-react
fix composition
PR welcome

Provide other platforms

Someone implement slate-vue but

PR Welcome

Appendix

Enable Tree Shaking

enable flag: usedExports or using production

Enable Tree Shaking

Mark files as side-effect-free

Enable Tree Shaking

No CommonJs, it's a joke

Enable Tree Shaking

Use resolve.mainFields of webpack correctly. Order is important.

Examples

only jsx-serializer, use commonjs from main field

Examples

only jsx-serializer, use es module from module field

References

Q & A

Rich Editor - quadrats

By jjaayy

Rich Editor - quadrats

  • 496