import React from 'react';
import { FloatingToolbarHandler, AlignType } from '../floating-toolbar/types';
import {
stateKey,
HyperlinkState,
InsertState,
EditInsertedState,
} from './pm-plugins/main';
import {
removeLink,
editInsertedLink,
updateLink,
insertLinkWithAnalytics,
} from './commands';
import HyperlinkAddToolbar from './ui/HyperlinkAddToolbar';
import { EditorView } from 'prosemirror-view';
import { Mark } from 'prosemirror-model';
import UnlinkIcon from '@atlaskit/icon/glyph/editor/unlink';
import OpenIcon from '@atlaskit/icon/glyph/shortcut';
import { normalizeUrl } from './utils';
import { EditorState } from 'prosemirror-state';
import {
linkToolbarMessages as linkToolbarCommonMessages,
linkMessages,
} from '../../messages';
import { isSafeUrl, LinkAttributes } from '@atlaskit/adf-schema';
import {
RECENT_SEARCH_HEIGHT_IN_PX,
RECENT_SEARCH_WIDTH_IN_PX,
} from '../../ui/LinkSearch/ToolbarComponents';
import { HyperlinkToolbarAppearance } from './HyperlinkToolbarAppearance';
/* type guard for edit links */
function isEditLink(
linkMark: EditInsertedState | InsertState,
): linkMark is EditInsertedState {
return (linkMark as EditInsertedState).pos !== undefined;
}
function getLinkText(
activeLinkMark: EditInsertedState,
state: EditorState,
): string | undefined | null {
if (!activeLinkMark.node) {
return undefined;
}
const textToUrl = normalizeUrl(activeLinkMark.node.text);
const linkMark = activeLinkMark.node.marks.find(
(mark: Mark) => mark.type === state.schema.marks.link,
);
const linkHref = linkMark && linkMark.attrs.href;
if (textToUrl === linkHref) {
return undefined;
}
return activeLinkMark.node.text;
}
export const getToolbarConfig: FloatingToolbarHandler = (
state,
intl,
providerFactory,
cardOptions,
) => {
const { formatMessage } = intl;
const linkState: HyperlinkState | undefined = stateKey.getState(state);
if (linkState && linkState.activeLinkMark) {
const { activeLinkMark } = linkState;
const hyperLinkToolbar = {
title: 'Hyperlink floating controls',
nodeType: [
state.schema.nodes.text,
state.schema.nodes.paragraph,
state.schema.nodes.heading,
state.schema.nodes.taskItem,
state.schema.nodes.decisionItem,
state.schema.nodes.caption,
].filter((nodeType) => !!nodeType), // Use only the node types existing in the schema ED-6745
align: 'left' as AlignType,
className: activeLinkMark.type.match('INSERT|EDIT_INSERTED')
? 'hyperlink-floating-toolbar'
: '',
};
switch (activeLinkMark.type) {
case 'EDIT': {
const { pos, node } = activeLinkMark;
const linkMark = node.marks.filter(
(mark) => mark.type === state.schema.marks.link,
);
const link = linkMark[0] && (linkMark[0].attrs as LinkAttributes).href;
const isValidUrl = isSafeUrl(link);
const labelOpenLink = formatMessage(
isValidUrl
? linkMessages.openLink
: linkToolbarCommonMessages.unableToOpenLink,
);
const labelUnlink = formatMessage(linkToolbarCommonMessages.unlink);
const editLink = formatMessage(linkToolbarCommonMessages.editLink);
let metadata = {
url: link,
title: '',
};
if (activeLinkMark.node.text) {
metadata.title = activeLinkMark.node.text;
}
return {
...hyperLinkToolbar,
height: 32,
width: 250,
items: [
{
type: 'custom',
fallback: [],
render: (editorView) => {
return (
);
},
},
{
id: 'editor.link.edit',
type: 'button',
onClick: editInsertedLink(),
selected: false,
title: editLink,
showTitle: true,
metadata: metadata,
},
{
type: 'separator',
},
{
id: 'editor.link.openLink',
type: 'button',
disabled: !isValidUrl,
target: '_blank',
href: isValidUrl ? link : undefined,
onClick: () => true,
selected: false,
title: labelOpenLink,
icon: OpenIcon,
className: 'hyperlink-open-link',
metadata: metadata,
},
{
type: 'separator',
},
{
id: 'editor.link.unlink',
type: 'button',
onClick: removeLink(pos),
selected: false,
title: labelUnlink,
icon: UnlinkIcon,
},
],
};
}
case 'EDIT_INSERTED':
case 'INSERT': {
let link: string;
if (isEditLink(activeLinkMark) && activeLinkMark.node) {
const linkMark = activeLinkMark.node.marks.filter(
(mark: Mark) => mark.type === state.schema.marks.link,
);
link = linkMark[0] && linkMark[0].attrs.href;
}
const displayText = isEditLink(activeLinkMark)
? getLinkText(activeLinkMark, state)
: linkState.activeText;
return {
...hyperLinkToolbar,
height: RECENT_SEARCH_HEIGHT_IN_PX,
width: RECENT_SEARCH_WIDTH_IN_PX,
items: [
{
type: 'custom',
fallback: [],
render: (
view?: EditorView,
idx?: number,
):
| React.ComponentClass
| React.SFC
| React.ReactElement
| null => {
if (!view) {
return null;
}
return (
{
isEditLink(activeLinkMark)
? updateLink(
href,
displayText || title,
activeLinkMark.pos,
)(view.state, view.dispatch)
: insertLinkWithAnalytics(
inputMethod,
activeLinkMark.from,
activeLinkMark.to,
href,
title,
displayText,
!!(cardOptions && cardOptions.provider),
)(view.state, view.dispatch);
view.focus();
}}
/>
);
},
},
],
};
}
}
}
return;
};