Skip to content

Instantly share code, notes, and snippets.

@andykent
Last active May 19, 2020 11:45
Show Gist options
  • Save andykent/202e819c35bd720e73302849c76fab33 to your computer and use it in GitHub Desktop.
Save andykent/202e819c35bd720e73302849c76fab33 to your computer and use it in GitHub Desktop.
Slate TypeScript Definitions
declare module 'slate-react' {
import * as Slate from 'slate'
import * as Immutable from 'immutable'
import { ReactNode } from 'react'
type RenderMarkProps = {
mark: { type: string },
children: ReactNode
}
type RenderNodeProps = {
node: Slate.Block,
children: ReactNode,
attributes: any,
isSelected: boolean,
editor: Editor
}
interface Plugin {
onBeforeInput?: (event: Event, change: Slate.Change, editor: Editor) => any | void
onBlur?: (event: Event, change: Slate.Change, editor: Editor) => any | void
onFocus?: (event: Event, change: Slate.Change, editor: Editor) => any | void
onCopy?: (event: Event, change: Slate.Change, editor: Editor) => any | void
onCut?: (event: Event, change: Slate.Change, editor: Editor) => any | void
onDrop?: (event: Event, change: Slate.Change, editor: Editor) => any | void
onKeyDown?: (event: Event, change: Slate.Change, editor: Editor) => any | void
onKeyUp?: (event: Event, change: Slate.Change, editor: Editor) => any | void
onPaste?: (event: Event, change: Slate.Change, editor: Editor) => any | void
onSelect?: (event: Event, change: Slate.Change, editor: Editor) => any | void
onChange?: (change: Slate.Change) => any | void
renderEditor?: (props: { [key: string]: any }, editor: Editor) => any | void
schema?: Slate.Schema
decorateNode?: (node: Slate.Node) => Slate.Range[] | void
renderMark?: (props: RenderMarkProps) => any
renderNode?: (props: RenderNodeProps) => any
renderPlaceholder?: (props: { [key: string]: any }) => any
renderPortal?: (props: { [key: string]: any }) => any
validateNode?: (node: Node) => any
}
type BasicEditorProps = {
value: Slate.Value
autoCorrect?: boolean,
autoFocus?: boolean,
className?: string,
onChange?: (change: Slate.Change) => void,
placeholder?: any,
plugins?: Plugin[],
readOnly?: boolean,
role?: string,
schema?: Slate.Schema,
spellCheck?: boolean,
style?: { [key: string]: string },
tabIndex?: number
}
type EditorProps = BasicEditorProps & Plugin
type EditorState = {
schema: Slate.Schema
value: Slate.Value
stack: Slate.Stack // [TODO] define stack
}
export class Editor extends React.Component<EditorProps, EditorState> {
schema: Slate.Schema
value: Slate.Value
stack: Slate.Stack
// Instance Methods
blur(): void
change(fn: (change: Slate.Change) => any | void): void
change(...args: any[]): void
focus(): void
}
type SlateType = 'fragment' | 'html' | 'node' | 'rich' | 'text'
export function findDOMNode(node: Slate.Node): Element
export function findDOMRange(range: Slate.Range): Range
export function findNode(element: Element, value: Slate.Value): Slate.Node
export function findRange(selection: Selection, value: Slate.Value): Slate.Range
export function getEventRange(event: Event, value: Slate.Value): Slate.Range
export function getEventTransfer(event: Event): { type: SlateType, node: Slate.Node }
export function setEventTransfer(event: Event, type: SlateType, data: any): void
}
declare module 'slate' {
import * as Immutable from 'immutable'
export interface Data {
[key: string]: any
}
interface Rules {
[key: string]: Rule
}
interface KindsAndTypes {
kinds?: string[]
types?: string[]
}
type InvalidReason = 'child_kind_invalid' | 'child_required' |
'child_type_invalid' | 'child_unknown' | 'first_child_kind_invalid' |
'first_child_type_invalid' | 'last_child_kind_invalid' | 'last_child_type_invalid' |
'node_data_invalid' | 'node_is_void_invalid' | 'node_mark_invalid' |
'node_text_invalid' | 'parent_kind_invalid' | 'parent_type_invalid'
interface Rule {
data?: {
[key: string]: (v: any) => boolean
}
first?: KindsAndTypes
isVoid?: boolean
last?: KindsAndTypes
nodes?: {
kinds?: string[]
types?: string[]
min?: number
max?: number
}[]
normalize?: (change: Change, reason: InvalidReason, context: { [key: string]: any }) => void
parent?: KindsAndTypes
text?: RegExp
}
interface SchemaProperties {
document?: Rules
blocks?: Rules
inlines?: Rules
}
export class Schema extends Immutable.Record({}) {
document: Rules
blocks: Rules
inlines: Rules
static create(properties: SchemaProperties | Schema): Schema
static fromJSON(object: SchemaProperties): Schema
static isSchema(maybeSchema: any): boolean
toJSON(): SchemaProperties
}
interface ValueProperties {
document?: Document
selection?: Range
history?: History
schema?: Schema
data?: Data
decorations?: Immutable.List<Range> | null
}
interface ValueJSON {
document?: DocumentJSON
selection?: Range
history?: History
schema?: Schema
data?: Data
decorations?: Immutable.List<Range> | null
kind?: 'value'
}
export class Value extends Immutable.Record({}) {
document: Document
selection: Range
history: History
schema: Schema
data: Data
kind: 'value'
decorations: Immutable.List<Range> | null
readonly anchorText: Text
readonly focusText: Text
readonly startText: Text
readonly endText: Text
readonly anchorBlock: Block
readonly focusBlock: Block
readonly startBlock: Block
readonly endBlock: Block
readonly marks: Immutable.Set<Mark>
readonly activeMarks: Immutable.Set<Mark>
readonly blocks: Immutable.List<Block>
readonly fragement: Document
readonly inlines: Immutable.List<Inline>
readonly text: Immutable.List<Text>
readonly characters: Immutable.List<Character>
readonly hasUndos: boolean
readonly hasRedos: boolean
readonly anchorKey: string
readonly focusKey: string
readonly startKey: string
readonly endKey: string
readonly anchorOffset: number
readonly focusOffset: number
readonly startOffset: number
readonly endOffset: number
readonly isBackward: boolean
readonly isBlurred: boolean
readonly isCollapsed: boolean
readonly isExpanded: boolean
readonly isFocused: boolean
readonly isForward: boolean
static create(properties: ValueProperties | Value): Value
static fromJSON(properties: ValueJSON): Value
static isValue(maybeValue: any): boolean
change(): Change
toJSON(): ValueJSON
}
interface DocumentProperties {
nodes?: Immutable.List<Node> | Node[]
key?: string
data?: Immutable.Map<string, any> | { [key: string]: any }
}
interface DocumentJSON {
nodes?: NodeJSON[]
key?: string
data?: { [key: string]: any }
kind: 'document'
}
export class Document extends BaseNode {
kind: 'document'
static create(properties: DocumentProperties | Document | Immutable.List<Node> | Node[]): Document
static fromJSON(properties: DocumentProperties | Document): Document
static isDocument(maybeDocument: any): boolean
toJSON(): DocumentJSON
}
interface BlockProperties {
type: string
key?: string
nodes?: Immutable.List<Node>
isVoid?: boolean
data?: Immutable.Map<string, any> | { [key: string]: any }
}
interface BlockJSON {
type: string
key?: string
nodes?: Node[]
isVoid?: boolean
data?: { [key: string]: any }
kind: 'block'
}
export class Block extends BaseNode {
isVoid: boolean
kind: 'block'
static create(properties: BlockProperties | Block | string): Block
static createList(array: (BlockProperties | Block | string)[]): Immutable.List<Block>
static fromJSON(properties: BlockProperties | Block): Block
static isBlock(maybeBlock: any): boolean
toJSON(): BlockJSON
}
interface InlineProperties {
type: string
key?: string
nodes?: Immutable.List<Node>
isVoid?: boolean
data?: Immutable.Map<string, any> | { [key: string]: any }
}
interface InlineJSON {
type: string
key?: string
nodes?: Node[]
isVoid?: boolean
data?: { [key: string]: any }
kind: 'inline'
}
export class Inline extends BaseNode {
isVoid: boolean
kind: 'inline'
static create(properties: InlineProperties | Inline | string): Inline
static createList(array: (InlineProperties | Inline | string)[]): Immutable.List<Inline>
static fromJSON(properties: InlineProperties | Inline): Inline
static isInline(maybeInline: any): boolean
toJSON(): InlineJSON
}
interface TextProperties {
key?: string
characters: Immutable.List<Character>
}
interface TextJSON {
key?: string
characters: Character[]
kind: 'text'
}
export class Text extends Immutable.Record({}) {
kind: 'text'
characters: Immutable.List<Character>
static create(properties: TextProperties | Text | string): Text
static fromJSON(properties: TextProperties | Text): Text
static isText(maybeText: any): boolean
toJSON(): TextJSON
}
type Node = Document | Block | Inline | Text
type NodeJSON = DocumentJSON | BlockJSON | InlineJSON | TextJSON
class BaseNode extends Immutable.Record({}) {
data: Immutable.Map<string, any>
type: string
key: string
kind: 'document' | 'block' | 'inline' | 'text'
nodes: Immutable.List<Node>
readonly text: string
filterDescendants(iterator: (node: Node) => boolean): Immutable.List<Node>
findDescendants(iterator: (node: Node) => boolean): Node | null
getBlocksAtRange(range: Range): Immutable.List<Block>
getBlocks(): Immutable.List<Block>
getCharactersAtRange(range: Range): Immutable.List<Character>
getChild(key: string | Node): Node | null
getClosestBlock(key: string | Node): Block | null
getClosestInline(key: string | Node): Inline | null
getClosest(key: string | Node, match: (node: Node) => boolean): Node | null
getDepth(key: string | Node): number
getDescendant(key: string | Node): Node | null
getFirstText(): Text | null
getFragmentAtRange(range: Range): Document
getFurthest(key: string, iterator: (node: Node) => boolean): Node | null
getFurthestAncestor(key: string): Node | null
getFurthestBlock(key: string): Block | null
getFurthestInline(key: string): Inline | null
getFurthestOnlyChildAncestor(key: string): Node | null
getInlinesAtRange(range: Range): Immutable.List<Inline>
getLastText(): Text | null
getMarksAtRange(range: Range): Immutable.Set<Mark>
getNextBlock(key: string | Node): Block | null
getNextSibling(key: string | Node): Node | null
getNextText(key: string | Node): Text | null
getParent(key: string | Node): Node | void
getPreviousBlock(key: string | Node): Block | void
getPreviousSibling(key: string | Node): Node | void
getPreviousText(key: string | Node): Text | void
getTextAtOffset(offset: Number): Text | void
getTextsAtRange(range: Range): Immutable.List<Text>
hasChild(key: string | Node): boolean
}
interface CharacterProperties {
marks?: Immutable.Set<Mark> | Mark[]
text: string
}
export class Character extends Immutable.Record({}) {
kind: 'character'
marks: Immutable.Set<Mark>
text: string
static create(properties: CharacterProperties | Character | string): Character
static createList(array: (CharacterProperties | Character | string)[]): Immutable.List<Character>
static fromJSON(properties: CharacterProperties | Character): Character
static isCharacter(maybeCharacter: any): boolean
toJSON(): CharacterProperties
}
interface MarkProperties {
type: string
data?: Immutable.Map<string, any> | { [key: string]: any }
}
export class Mark extends Immutable.Record({}) {
kind: 'mark'
type: string
data: Immutable.Map<string, any>
static create(properties: MarkProperties | Mark | string): Mark
static createSet(array: (MarkProperties | Mark | string)[]): Immutable.Set<Mark>
static fromJSON(properties: MarkProperties | Mark): Mark
static isMark(maybeMark: any): boolean
toJSON(): MarkProperties
}
export class Change extends Immutable.Record({}) {
kind: 'change'
value: Value
operations: Immutable.List<Operation>
call(customChange: (change: Change, ...args: any[]) => Change): Change
applyOperations(operations: Operation[]): Change
applyOperation(operation: Operation): Change
// Current Value Changes
deleteBackward(n: number): Change
deleteForward(n: number): Change
delete(): Change
insertBlock(block: Block | BlockProperties | string): Change
insertFragment(fragment: Document): Change
insertInline(inline: Inline | InlineProperties): Change
insertText(text: string): Change
addMark(mark: Mark | MarkProperties | string): Change
setBlock(properties: BlockProperties | string): Change
setInline(properties: InlineProperties | string): Change
splitBlock(depth: number): Change
splitInline(depth: number): Change
removeMark(mark: Mark | MarkProperties | string): Change
toggleMark(mark: Mark | MarkProperties | string): Change
unwrapBlock(properties: BlockProperties | string): Change
unwrapInline(properties: InlineProperties | string): Change
wrapBlock(properties: BlockProperties | string): Change
wrapInline(properties: InlineProperties | string): Change
wrapText(prefix: string, suffix?: string): Change
// Selection Changes
blur(): Change
collapseToAnchor(): Change
collapseToFocus(): Change
collapseToSart(): Change
collapseToEnd(): Change
collapseToSartOf(node: Node): Change
collapseToEndOf(node: Node): Change
collapseToSartOfNextBlock(): Change
collapseToEndOfNextBlock(): Change
collapseToSartOfPreviousBlock(): Change
collapseToEndOfPreviousBlock(): Change
collapseToSartOfNextText(): Change
collapseToEndOfNextText(): Change
collapseToSartOfPreviousText(): Change
collapseToEndOfPreviousText(): Change
extend(n: number): Change
extendToStartOf(node: Node): Change
extendToEndOf(node: Node): Change
flip(): Change
focus(): Change
move(n: number): Change
moveStart(n: number): Change
moveEnd(n: number): Change
moveOffsetsTo(anchorOffset: number, focusOffset: number): Change
moveToRangeOf(node: Node): Change
select(properties: Range | RangeProperties): Change
selectAll(): Change
deselect(): Change
// Document Changes
deleteBackwardAtRange(range: Range, n: number): Change
deleteForwardAtRange(range: Range, n: number): Change
deleteAtRange(range: Range): Change
insertBlockAtRange(range: Range, block: Block | BlockProperties | string): Change
insertFragmentAtRange(range: Range, fragment: Document): Change
insertInlineAtRange(range: Range, inline: Inline | InlineProperties): Change
insertTextAtRange(range: Range, text: string): Change
addMarkAtRange(range: Range, mark: Mark | MarkProperties | string): Change
setBlockAtRange(range: Range, properties: BlockProperties | string): Change
setInlineAtRange(range: Range, properties: InlineProperties | string): Change
splitBlockAtRange(range: Range, depth: number): Change
splitInlineAtRange(range: Range, depth: number): Change
removeMarkAtRange(range: Range, mark: Mark | MarkProperties | string): Change
toggleMarkAtRange(range: Range, mark: Mark | MarkProperties | string): Change
unwrapBlockAtRange(range: Range, properties: BlockProperties | string): Change
unwrapInlineAtRange(range: Range, properties: InlineProperties | string): Change
wrapBlockAtRange(range: Range, properties: BlockProperties | string): Change
wrapInlineAtRange(range: Range, properties: InlineProperties | string): Change
wrapTextAtRange(range: Range, prefix: string, suffix?: string): Change
// Node Changes
addMarkByKey(key: string, offset: number, length: number, mark: Mark): Change
insertNodeByKey(key: string, index: number, node: Node): Change
insertFragmentByKey(key: string, index: number, fragment: Document): Change
insertTextByKey(key: string, offset: number, text: string, marks?: (Immutable.Set<Mark> | Mark[])): Change
moveNodeByKey(key: string, newKey: string, newIndex: number): Change
removeMarkByKey(key: string, offset: number, length: number, mark: Mark): Change
removeNodeByKey(key: string): Change
replaceNodeByKey(key: string, node: Node): Change
removeTextByKey(key: string, offset: number, length: number): Change
setMarkByKey(key: string, offset: number, length: number, mark: Mark, properties: MarkProperties): Change
setNodeByKey(key: string, properties: BlockProperties | InlineProperties | string): Change
splitNodeByKey(key: string, offset: number): Change
unwrapInlineByKey(key: string, properties: InlineProperties | string): Change
unwrapBlockByKey(key: string, properties: BlockProperties | string): Change
unwrapNodeByKey(key: string): Change
wrapInlineByKey(key: string, properties: InlineProperties | string): Change
wrapBlockByKey(key: string, properties: BlockProperties | string): Change
// History Changes
redo(): Change
undo(): Change
}
interface RangeProperties {
anchorKey?: string | null
anchorOffset?: number
focusKey?: string | null
focusOffset?: number
isFocused?: boolean
isBackward?: boolean | null
marks?: Immutable.Set<Mark> | null
}
export class Range extends Immutable.Record({}) {
kind: 'range'
anchorKey: string | null
anchorOffset: number
focusKey: string | null
focusOffset: number
isFocused: boolean
isBackward: boolean | null
marks: Immutable.Set<Mark> | null
readonly isBlurred: boolean
readonly isCollapsed: boolean
readonly isExpanded: boolean
readonly isForward: boolean
readonly startKey: string
readonly startOffset: number
readonly endKey: string
readonly endOffset: number
static create(properties: RangeProperties | Range): Range
static fromJSON(properties: RangeProperties): Range
static isRange(maybeRange: any): boolean
toJSON(): RangeProperties
hasAnchorAtStartOf(node: Node): boolean
hasFocusAtStartOf(node: Node): boolean
hasStartAtStartOf(node: Node): boolean
hasEndAtStartOf(node: Node): boolean
hasEdgeAtStartOf(node: Node): boolean
hasAnchorAtEndOf(node: Node): boolean
hasFocusAtEndOf(node: Node): boolean
hasStartAtEndOf(node: Node): boolean
hasEndAtEndOf(node: Node): boolean
hasEdgeAtEndOf(node: Node): boolean
hasAnchorBetween(node: Node, start: number, end: number): boolean
hasFocusBetween(node: Node, start: number, end: number): boolean
hasStartBetween(node: Node, start: number, end: number): boolean
hasEndBetween(node: Node, start: number, end: number): boolean
hasEdgeBetween(node: Node, start: number, end: number): boolean
hasAnchorAtIn(node: Node): boolean
hasFocusIn(node: Node): boolean
hasStartIn(node: Node): boolean
hasEndIn(node: Node): boolean
hasEdgeIn(node: Node): boolean
isAtStartOf(node: Node): boolean
isAtEndOf(node: Node): boolean
}
type Operation = InsertTextOperation | RemoveTextOperation | AddMarkOperation |
RemoveMarkOperation | SetMarkOperation | InsertNodeOperation | MergeNodeOperation |
MoveNodeOperation | RemoveNodeOperation | SetNodeOperation | SplitNodeOperation |
SetSelectionOperation | SetValueOperation
type InsertTextOperation = {
type: 'insert_text'
path: number[]
offset: number
text: string
marks: Mark[]
}
type RemoveTextOperation = {
type: 'remove_text'
path: number[]
offset: number
text: string
}
type AddMarkOperation = {
type: 'add_mark'
path: number[]
offset: number
length: number
mark: Mark
}
type RemoveMarkOperation = {
type: 'remove_mark'
path: number[]
offset: number
length: number
mark: Mark
}
type SetMarkOperation = {
type: 'set_mark'
path: number[]
offset: number
length: number
mark: Mark
properties: MarkProperties
}
type InsertNodeOperation = {
type: 'insert_node'
path: number[]
node: Node
}
type MergeNodeOperation = {
type: 'merge_node'
path: number[]
position: number
}
type MoveNodeOperation = {
type: 'move_node'
path: number[]
newPath: number[]
}
type RemoveNodeOperation = {
type: 'remove_node'
path: number[]
node: Node
}
type SetNodeOperation = {
type: 'set_node'
path: number[]
properties: BlockProperties | InlineProperties | TextProperties
}
type SplitNodeOperation = {
type: 'split_node'
path: number[]
position: number
target: number
}
type SetSelectionOperation = {
type: 'set_selection'
properties: RangeProperties
selection: Range
}
type SetValueOperation = {
type: 'set_value'
properties: ValueProperties
value: Value
}
export const Operations: {
apply: (value: Value, operation: Operation) => Value
invert: (operation: Operation) => Operation
}
export class Stack extends Immutable.Record({}) {
plugins: any[]
}
export function resetKeyGenerator(): void
}
@pmairoldi
Copy link

For the Rule interface I believe the nodes property should be an array like the following:

  interface Rule {
    data?: {
      [key: string]: (v: any) => boolean
    }
    first?: KindsAndTypes
    isVoid?: boolean
    last?: KindsAndTypes
    nodes?: {
      kinds?: string[]
      types?: string[]
      min?: number
      max?: number
    }[]
    normalize?: (change: Change, reason: InvalidReason, context: { [key: string]: any }) => void
    parent?: KindsAndTypes
    text?: RegExp
  }

Have you thought about create a @types repo for these? They are really good.

@andykent
Copy link
Author

Thanks for the feedback @petester42, good spot.

I've made the suggested change as well as some other tweaks and fixes just now.

I'd be very happy to see them on @types I don't have the time submit them myself right now but if anyone else wanted to use these as a starting point to put together a PR I'd be very happy about that. If not hopefully I'll find some time to contribute them to the project soon.

@majelbstoat
Copy link

majelbstoat commented Mar 28, 2018

Heads up, you have a typo in the collapseToSart* rules, @andykent.

But, you just saved me hours and hours of work, and probably dozens of future bugs. Beers on me if you're ever in San Francisco. And either way, if you tell me a charity of your choice, I'll make a donation there too. (I'll default to Great Ormond Street if I don't hear back in a little while.)

Update 7th April 2018:

screenshot 2018-04-07 15 56 02

@majelbstoat
Copy link

I fixed a couple of typos, updated the is* methods to be type guards, and added a couple of missing methods around getting text: https://gist.github.com/majelbstoat/e3f3a886f9f79ac6b5286b353b77f291

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment