@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
182 lines (181 loc) • 6.64 kB
JavaScript
import { buildAction } from './manifest-helpers';
export const groupBy = (arr, attr, keyRenamer) => arr.reduce((acc, item) => {
acc[keyRenamer(item[attr])] = item;
return acc;
}, {});
export function buildMenuItem(manifest, extensionModule) {
const title = extensionModule.title || manifest.title;
const key = `${manifest.key}:${extensionModule.key}`;
const node = buildAction(extensionModule.action, manifest);
if (!node) {
throw new Error(`Couldn't find any action for ${title} (${key})`);
}
return {
key,
title,
extensionType: manifest.type,
keywords: extensionModule.keywords || manifest.keywords || [],
featured: extensionModule.featured || false,
categories: extensionModule.categories || manifest.categories || [],
description: extensionModule.description || manifest.description,
summary: manifest.summary,
documentationUrl: manifest.documentationUrl,
icon: extensionModule.icon || manifest.icons['48'],
node
};
}
export const getQuickInsertItemsFromModule = (extensions, transformFunction) => {
const items = extensions.map(manifest => {
const modules = manifest.modules.quickInsert || [];
return modules.map(extensionModule => buildMenuItem(manifest, extensionModule));
});
const flatItems = [].concat(...items);
return flatItems.map(transformFunction);
};
export async function getAutoConvertPatternsFromModule(extensions) {
const items = await Promise.all(extensions.map(async manifest => {
if (manifest.modules.autoConvert && manifest.modules.autoConvert.url) {
return manifest.modules.autoConvert.url;
}
return [];
}));
return [].concat(...items);
}
export const createAutoConverterRunner = autoConvertHandlers => text => {
for (const handler of autoConvertHandlers) {
const result = handler(text);
if (result) {
return result;
}
}
};
export async function getExtensionAutoConvertersFromProvider(extensionProviderPromise) {
const extensionProvider = await extensionProviderPromise;
const extensionAutoConverters = await extensionProvider.getAutoConverter();
return createAutoConverterRunner(extensionAutoConverters);
}
const logError = (msg, ...args) => {
// eslint-disable-next-line no-console
console.error(msg, ...args);
};
const toolbarItemToButtonConfig = (toolbarItem, parentKey) => {
const {
tooltip,
key,
label,
icon,
action,
disabled
} = toolbarItem;
const itemKey = [parentKey, key].join(':');
if (typeof action !== 'function') {
logError(`Provided action is not a function for extension toolbar button: ${label} (${itemKey})`);
}
let labelAndIcon = {};
switch (toolbarItem.display) {
case 'icon':
if (!icon) {
logError(`icon should be provided for extension toolbar button (${itemKey}), when display is set to 'icon'`);
}
labelAndIcon = {
icon
};
break;
case 'label':
if (!label) {
logError(`label should be provided for extension toolbar button (${itemKey}), when display is set to 'label'`);
}
labelAndIcon = {
label
};
break;
default:
if (!label || !icon) {
logError(`label and icon should be provided for extension toolbar button (${itemKey}), when display is not set or set to 'icon-and-label'`);
}
labelAndIcon = {
icon,
label
};
break;
}
return {
key: itemKey,
tooltip,
action,
disabled,
...labelAndIcon
};
};
const compareContext = (contextA, contextB) => {
if (contextA.type === contextB.type && contextA.nodeType === contextB.nodeType) {
if (contextA.type === 'node') {
return true;
}
if (contextA.type === 'extension' && contextB.type === 'extension') {
return contextA.extensionKey === contextB.extensionKey && contextA.extensionType === contextB.extensionType;
}
}
return false;
};
const hasDuplicateContext = contextualToolbars => {
if (contextualToolbars.length > 1) {
const contexts = contextualToolbars.map(contextualToolbar => contextualToolbar.context);
return contexts.find((currentContext, currentIndex) => currentIndex !== contexts.findIndex(context => compareContext(currentContext, context)));
}
};
export const getContextualToolbarItemsFromModule = (extensions, node, api) => {
return extensions.map(extension => {
if (extension.modules.contextualToolbars) {
const duplicateContext = hasDuplicateContext(extension.modules.contextualToolbars);
if (duplicateContext) {
logError(`[contextualToolbars] Duplicate context detected - ${JSON.stringify(duplicateContext)}.`);
return [];
}
return extension.modules.contextualToolbars.map(contextualToolbar => {
if (shouldAddExtensionItemForNode(contextualToolbar, node)) {
const {
toolbarItems
} = contextualToolbar;
if (typeof toolbarItems === 'function') {
return toolbarItems(node, api);
}
return toolbarItems;
}
return [];
}).flatMap(toolbarButtons => toolbarButtons.map(toolbarButton => toolbarItemToButtonConfig(toolbarButton, extension.key)));
}
return [];
}).flatMap(extensionToolbarButtons => extensionToolbarButtons);
};
// defines whether to add toolbar item for the given node
function shouldAddExtensionItemForNode(item, node) {
var _node$attrs;
// if item type is a standard node - should match the nodeType
if (item.context.type === 'node' && item.context.nodeType === node.type) {
return true;
}
// for other cases should be an extension and match the nodeType ("extension" | "inlineExtension" | "bodiedExtension" | "multiBodiedExtension")
if (item.context.type !== 'extension' || node.type !== item.context.nodeType) {
return false;
}
const {
extensionType,
extensionKey
} = item.context;
// if extension type is given - should match extension type
if (extensionType && extensionType !== ((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.extensionType)) {
return false;
}
// if extension key is a string
if (typeof extensionKey === 'string') {
var _node$attrs2;
return extensionKey === ((_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.extensionKey);
}
// if extension key is an array
if (Array.isArray(extensionKey) && extensionKey.length) {
var _node$attrs3;
return extensionKey.includes((_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.extensionKey);
}
return false;
}