prosemirror-view: make decorations generic (#38620)

This commit is contained in:
Ifiok Jr 2019-09-30 21:03:57 +01:00 committed by Michael Crane
parent 3d6e86fd92
commit 02c840cd68
2 changed files with 87 additions and 67 deletions

View File

@ -20,12 +20,73 @@ import {
import { EditorState, Selection, Transaction } from 'prosemirror-state';
import { Mapping } from 'prosemirror-transform';
/**
* The `spec` for a widget decoration
*/
export interface WidgetDecorationSpec {
/**
* Controls which side of the document position this widget is
* associated with. When negative, it is drawn before a cursor
* at its position, and content inserted at that position ends
* up after the widget. When zero (the default) or positive, the
* widget is drawn after the cursor and content inserted there
* ends up before the widget.
*
* When there are multiple widgets at a given position, their
* `side` values determine the order in which they appear. Those
* with lower values appear first. The ordering of widgets with
* the same `side` value is unspecified.
*
* When `marks` is null, `side` also determines the marks that
* the widget is wrapped inthose of the node before when
* negative, those of the node after when positive.
*/
side?: number | null;
/**
* The precise set of marks to draw around the widget.
*/
marks?: Mark[] | null;
/**
* Can be used to control which DOM events, when they bubble out
* of this widget, the editor view should ignore.
*/
stopEvent?: ((event: Event) => boolean) | null;
/**
* When comparing decorations of this type (in order to decide
* whether it needs to be redrawn), ProseMirror will by default
* compare the widget DOM node by identity. If you pass a key,
* that key will be compared instead, which can be useful when
* you generate decorations on the fly and don't want to store
* and reuse DOM nodes. Make sure that any widgets with the same
* key are interchangeableif widgets differ in, for example,
* the behavior of some event handler, they should get
* different keys.
*/
key?: string | null;
}
/**
* The `spec` for the inline decoration.
*/
export interface InlineDecorationSpec {
/**
* Determines how the left side of the decoration is
* [mapped](#transform.Position_Mapping) when content is
* inserted directly at that position. By default, the decoration
* won't include the new content, but you can set this to `true`
* to make it inclusive.
*/
inclusiveStart?: boolean | null;
/**
* Determines how the right side of the decoration is mapped.
*/
inclusiveEnd?: boolean | null;
}
/**
* Decoration objects can be provided to the view through the
* [`decorations` prop](#view.EditorProps.decorations). They come in
* several variantssee the static members of this class for details.
*/
export class Decoration {
export class Decoration<T extends object = { [key: string]: any }> {
/**
* The start position of the decoration.
*/
@ -39,7 +100,7 @@ export class Decoration {
* The spec provided when creating this decoration. Can be useful
* if you've stored extra information in that object.
*/
spec: { [key: string]: any };
spec: T;
/**
* Creates a widget decoration, which is a DOM node that's shown in
* the document at the given position. It is recommended that you
@ -48,87 +109,32 @@ export class Decoration {
* also directly pass a DOM node. getPos can be used to find the
* widget's current document position.
*/
static widget(
static widget<T extends object = { [key: string]: any }>(
pos: number,
toDOM: ((view: EditorView, getPos: () => number) => Node) | Node,
spec?: {
/**
* Controls which side of the document position this widget is
* associated with. When negative, it is drawn before a cursor
* at its position, and content inserted at that position ends
* up after the widget. When zero (the default) or positive, the
* widget is drawn after the cursor and content inserted there
* ends up before the widget.
*
* When there are multiple widgets at a given position, their
* `side` values determine the order in which they appear. Those
* with lower values appear first. The ordering of widgets with
* the same `side` value is unspecified.
*
* When `marks` is null, `side` also determines the marks that
* the widget is wrapped inthose of the node before when
* negative, those of the node after when positive.
*/
side?: number | null;
/**
* The precise set of marks to draw around the widget.
*/
marks?: Mark[] | null;
/**
* Can be used to control which DOM events, when they bubble out
* of this widget, the editor view should ignore.
*/
stopEvent?: ((event: Event) => boolean) | null;
/**
* When comparing decorations of this type (in order to decide
* whether it needs to be redrawn), ProseMirror will by default
* compare the widget DOM node by identity. If you pass a key,
* that key will be compared instead, which can be useful when
* you generate decorations on the fly and don't want to store
* and reuse DOM nodes. Make sure that any widgets with the same
* key are interchangeableif widgets differ in, for example,
* the behavior of some event handler, they should get
* different keys.
*/
key?: string | null;
[key: string]: any;
}
): Decoration;
spec?: T & WidgetDecorationSpec
): Decoration<T & WidgetDecorationSpec>;
/**
* Creates an inline decoration, which adds the given attributes to
* each inline node between `from` and `to`.
*/
static inline(
static inline<T extends object = { [key: string]: any }>(
from: number,
to: number,
attrs: DecorationAttrs,
spec?: {
/**
* Determines how the left side of the decoration is
* [mapped](#transform.Position_Mapping) when content is
* inserted directly at that position. By default, the decoration
* won't include the new content, but you can set this to `true`
* to make it inclusive.
*/
inclusiveStart?: boolean | null;
/**
* Determines how the right side of the decoration is mapped.
*/
inclusiveEnd?: boolean | null
[key: string]: any;
}
): Decoration;
spec?: T & InlineDecorationSpec
): Decoration<T & InlineDecorationSpec>;
/**
* Creates a node decoration. `from` and `to` should point precisely
* before and after a node in the document. That node, and only that
* node, will receive the given attributes.
*/
static node(
static node<T extends object = { [key: string]: any }>(
from: number,
to: number,
attrs: DecorationAttrs,
spec?: { [key: string]: any }
): Decoration;
spec?: T
): Decoration<T>;
}
/**
* A set of attributes to add to a decorated node. Most properties

View File

@ -1,7 +1,21 @@
import * as view from 'prosemirror-view';
import * as state from 'prosemirror-state';
const decoration = new view.Decoration();
const { Decoration } = view;
const decoration = new Decoration();
const plainDecoration = new Decoration<{ a: number }>();
plainDecoration.spec.a; // $ExpectType number
Decoration.widget<{a: number}>(0, (x) => x.dom, { a: '' }); // $ExpectError
Decoration.widget(0, (x) => x.dom, 'this argument should be an object, not a string!'); // $ExpectError
const widgetDecoration = Decoration.widget<{ a: number }>(0, (x) => x.dom, { a: 1 });
widgetDecoration.spec.a; // $ExpectType number
widgetDecoration.spec.stopEvent; // $ExpectType ((event: Event) => boolean) | null | undefined
Decoration.node<{ a: number }>(0, 10, {}, { a: '' }); // $ExpectError
const nodeDecoration = Decoration.node<{ a: number }>(0, 10, {}, { a: 1 });
nodeDecoration.spec.a; // $ExpectType number
const res1_1 = new view.EditorView({} as any, {} as any);
const res1_2: { pos: number, inside: number } = res1_1.posAtCoords({ left: 0, top: 0})!;