@atlaskit/editor-core
Version:
A package contains Atlassian editor core functionality
182 lines (163 loc) • 5.12 kB
text/typescript
import { NodeSpec, MarkSpec, Schema } from '../prosemirror';
import { COLOR, FONT_STYLE, SEARCH_QUERY, LINK } from './groups';
import {
link,
em,
strong,
strike,
subsup,
underline,
code,
mentionQuery,
emojiQuery,
textColor,
} from './marks';
import {
confluenceJiraIssue,
confluenceUnsupportedBlock,
confluenceUnsupportedInline,
doc,
paragraph,
text,
bulletList,
orderedList,
listItem,
heading,
blockquote,
codeBlock,
panel,
rule,
image,
mention,
media,
mediaGroup,
singleImage,
hardBreak,
emoji,
table,
tableCell,
tableHeader,
tableRow,
applicationCard,
decisionList,
decisionItem,
taskList,
taskItem,
unknownBlock,
} from './nodes';
function addItems(builtInItems: SchemaBuiltInItem[], config: string[], customSpecs: SchemaCustomNodeSpecs | SchemaCustomMarkSpecs = {}) {
if (!config) {
return {};
}
/**
* Add built-in Node / Mark specs
*/
const items = builtInItems.reduce((items, { name, spec }) => {
if (config.indexOf(name) !== -1) {
items[name] = customSpecs[name] || spec;
}
return items;
}, {});
/**
* Add Custom Node / Mark specs
*/
return Object.keys(customSpecs).reduce((items, name) => {
if (items[name]) {
return items;
}
items[name] = customSpecs[name];
return items;
}, items);
}
// We use groups to allow schemas to be constructed in different shapes without changing node/mark
// specs, but this means nodes/marks are defined with groups that might never be used in the schema.
// In this scenario ProseMirror will complain and prevent the schema from being constructed.
//
// To avoid the problem, we include items that serve to "declare" the groups in the schema. This
// approach unfortunately leaves unused items in the schema, but has the benefit of avoiding the
// need to manipulate `exclude` or content expression values for potentially every schema item.
function groupDeclaration(name: string) {
return {
name: `__${name}GroupDeclaration`,
spec: {
group: name,
}
};
}
const markGroupDeclarations = [
groupDeclaration(COLOR),
groupDeclaration(FONT_STYLE),
groupDeclaration(SEARCH_QUERY),
groupDeclaration(LINK),
];
const markGroupDeclarationsNames = markGroupDeclarations.map(groupMark => groupMark.name);
const nodesInOrder: SchemaBuiltInItem[] = [
{ name: 'doc', spec: doc },
{ name: 'paragraph', spec: paragraph },
{ name: 'text', spec: text },
{ name: 'bulletList', spec: bulletList },
{ name: 'orderedList', spec: orderedList },
{ name: 'listItem', spec: listItem },
{ name: 'heading', spec: heading },
{ name: 'blockquote', spec: blockquote },
{ name: 'codeBlock', spec: codeBlock },
{ name: 'panel', spec: panel },
{ name: 'rule', spec: rule },
{ name: 'image', spec: image },
{ name: 'mention', spec: mention },
{ name: 'media', spec: media },
{ name: 'mediaGroup', spec: mediaGroup },
{ name: 'singleImage', spec: singleImage },
{ name: 'hardBreak', spec: hardBreak },
{ name: 'emoji', spec: emoji },
{ name: 'table', spec: table },
{ name: 'tableCell', spec: tableCell },
{ name: 'tableRow', spec: tableRow },
{ name: 'tableHeader', spec: tableHeader },
{ name: 'confluenceJiraIssue', spec: confluenceJiraIssue },
{ name: 'confluenceUnsupportedInline', spec: confluenceUnsupportedInline },
{ name: 'confluenceUnsupportedBlock', spec: confluenceUnsupportedBlock },
{ name: 'applicationCard', spec: applicationCard },
{ name: 'decisionList', spec: decisionList },
{ name: 'decisionItem', spec: decisionItem },
{ name: 'taskList', spec: taskList },
{ name: 'taskItem', spec: taskItem },
{ name: 'unknownBlock', spec: unknownBlock },
];
const marksInOrder: SchemaBuiltInItem[] = [
{ name: 'link', spec: link },
{ name: 'em', spec: em },
{ name: 'strong', spec: strong },
{ name: 'strike', spec: strike },
{ name: 'subsup', spec: subsup },
{ name: 'underline', spec: underline },
{ name: 'code', spec: code },
{ name: 'mentionQuery', spec: mentionQuery },
{ name: 'emojiQuery', spec: emojiQuery },
{ name: 'textColor', spec: textColor },
...markGroupDeclarations,
];
/**
* Creates a schema preserving order of marks and nodes.
*/
export function createSchema(config: SchemaConfig): Schema<any, any> {
const { nodes, customNodeSpecs, marks, customMarkSpecs } = config;
const nodesConfig = Object.keys(customNodeSpecs || {}).concat(nodes);
const marksConfig = Object.keys(customMarkSpecs || {}).concat(marks || []).concat(markGroupDeclarationsNames);
return new Schema({
nodes: addItems(nodesInOrder, nodesConfig, customNodeSpecs),
marks: addItems(marksInOrder, marksConfig, customMarkSpecs),
});
}
export interface SchemaConfig {
nodes: string[];
customNodeSpecs?: SchemaCustomNodeSpecs;
marks?: string[];
customMarkSpecs?: SchemaCustomMarkSpecs;
}
export interface SchemaBuiltInItem {
name: string;
spec: NodeSpec | MarkSpec;
}
export interface SchemaCustomNodeSpecs { [name: string]: NodeSpec; }
export interface SchemaCustomMarkSpecs { [name: string]: MarkSpec; }