@atlaskit/editor-core
Version:
A package contains Atlassian editor core functionality
149 lines (121 loc) • 3.83 kB
text/typescript
import {
EditorState,
EditorView,
Schema,
findWrapping,
NodeSelection,
Plugin,
PluginKey,
} from '../../prosemirror';
import {
findAncestorPosition,
} from '../../utils';
import { toggleList } from './commands';
import keymapPlugin from './keymap';
import inputRulePlugin from './input-rule';
export type StateChangeHandler = (state: ListsState) => any;
/**
*
* Plugin State
*
*/
export class ListsState {
private changeHandlers: StateChangeHandler[] = [];
// public state
bulletListActive = false;
bulletListDisabled = false;
bulletListHidden = false;
orderedListActive = false;
orderedListDisabled = false;
orderedListHidden = false;
constructor(state: EditorState<any>) {
this.changeHandlers = [];
// Checks what types of lists schema supports.
const { bulletList, orderedList } = state.schema.nodes;
this.bulletListHidden = !bulletList;
this.orderedListHidden = !orderedList;
}
subscribe(cb: StateChangeHandler) {
this.changeHandlers.push(cb);
cb(this);
}
unsubscribe(cb: StateChangeHandler) {
this.changeHandlers = this.changeHandlers.filter(ch => ch !== cb);
}
toggleBulletList(view: EditorView): boolean {
return toggleList(view.state, view.dispatch, view, 'bulletList');
}
toggleOrderedList(view: EditorView): boolean {
return toggleList(view.state, view.dispatch, view, 'orderedList');
}
update(newEditorState) {
const { doc, selection } = newEditorState;
const ancestorPosition = findAncestorPosition(doc, selection.$from);
const rootNode = selection instanceof NodeSelection
? selection.node
: ancestorPosition.node(ancestorPosition.depth)!;
let dirty = false;
const newBulletListActive = rootNode.type === newEditorState.schema.nodes.bulletList;
if (newBulletListActive !== this.bulletListActive) {
this.bulletListActive = newBulletListActive;
dirty = true;
}
const newOrderedListActive = rootNode.type === newEditorState.schema.nodes.orderedList;
if (newOrderedListActive !== this.orderedListActive) {
this.orderedListActive = newOrderedListActive;
dirty = true;
}
const newBulletListDisabled = !(
newBulletListActive ||
newOrderedListActive ||
this.isWrappingPossible(newEditorState.schema.nodes.bulletList, newEditorState)
);
if (newBulletListDisabled !== this.bulletListDisabled) {
this.bulletListDisabled = newBulletListDisabled;
dirty = true;
}
const newOrderedListDisabled = !(
newBulletListActive ||
newOrderedListActive ||
this.isWrappingPossible(newEditorState.schema.nodes.orderedList, newEditorState)
);
if (newOrderedListDisabled !== this.orderedListDisabled) {
this.orderedListDisabled = newOrderedListDisabled;
dirty = true;
}
if (dirty) {
this.triggerOnChange();
}
}
private triggerOnChange() {
this.changeHandlers.forEach(cb => cb(this));
}
private isWrappingPossible(nodeType, state) {
const { $from, $to } = state.selection;
const range = $from.blockRange($to);
if (!range) { return false; }
const wrap = findWrapping(range, nodeType);
if (!wrap) { return false; }
return true;
}
}
export const stateKey = new PluginKey('listsPlugin');
export const plugin = new Plugin({
state: {
init(config, state: EditorState<any>) {
return new ListsState(state);
},
apply(tr, pluginState: ListsState, oldState, newState) {
pluginState.update(newState);
return pluginState;
}
},
key: stateKey,
view: (view: EditorView) => {
return {};
}
});
const plugins = (schema: Schema<any, any>) => {
return [plugin, inputRulePlugin(schema), keymapPlugin(schema)].filter((plugin) => !!plugin) as Plugin[];
};
export default plugins;