UNPKG

@atlaskit/editor-plugin-copy-button

Version:

editor-plugin-copy-button for @atlaskit/editor-core

153 lines (149 loc) • 6.71 kB
import { ACTION, INPUT_METHOD } from '@atlaskit/editor-common/analytics/types/enums'; import { copyHTMLToClipboard, getAnalyticsPayload } from '@atlaskit/editor-common/clipboard'; import { copyDomNode, getSelectedNodeOrNodeParentByNodeType, toDOM } from '@atlaskit/editor-common/copy-button'; import { copyButtonPluginKey } from './plugin-key'; export function createToolbarCopyCommandForMark(markType, editorAnalyticsApi) { function command(state, dispatch) { var textNode = state.tr.selection.$head.parent.maybeChild(state.tr.selection.$head.index()); if (!textNode) { return false; } if (dispatch) { // As calling copyHTMLToClipboard causes side effects -- we only run this when // dispatch is provided -- as otherwise the consumer is only testing to see if // the action is availble. var domNode = toDOM(textNode, state.schema); if (domNode) { var div = document.createElement('div'); var p = document.createElement('p'); div.appendChild(p); p.appendChild(domNode); // The "1 1" refers to the start and end depth of the slice // since we're copying the text inside a paragraph, it will always be 1 1 // https://github.com/ProseMirror/prosemirror-view/blob/master/src/clipboard.ts#L32 // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting div.firstChild.setAttribute('data-pm-slice', '1 1 []'); // If we're copying a hyperlink, we'd copy the url as the fallback plain text // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting var linkUrl = domNode.getAttribute('href'); copyHTMLToClipboard(div, markType.name === 'link' && linkUrl ? linkUrl : undefined); } var copyToClipboardTr = state.tr; copyToClipboardTr.setMeta(copyButtonPluginKey, { copied: true }); var analyticsPayload = getAnalyticsPayload(state, ACTION.COPIED); if (analyticsPayload && editorAnalyticsApi) { var _editorAnalyticsApi$a; analyticsPayload.attributes.inputMethod = INPUT_METHOD.FLOATING_TB; analyticsPayload.attributes.markType = markType.name; editorAnalyticsApi === null || editorAnalyticsApi === void 0 || (_editorAnalyticsApi$a = editorAnalyticsApi.attachAnalyticsEvent) === null || _editorAnalyticsApi$a === void 0 || _editorAnalyticsApi$a.call(editorAnalyticsApi, analyticsPayload)(copyToClipboardTr); } dispatch(copyToClipboardTr); } return true; } return command; } export function getProvideMarkVisualFeedbackForCopyButtonCommand(markType) { function provideMarkVisualFeedbackForCopyButtonCommand(state, dispatch) { var tr = state.tr; tr.setMeta(copyButtonPluginKey, { showSelection: true, markType: markType }); if (dispatch) { dispatch(tr); } return true; } return provideMarkVisualFeedbackForCopyButtonCommand; } export function removeMarkVisualFeedbackForCopyButtonCommand(state, dispatch) { var tr = state.tr; tr.setMeta(copyButtonPluginKey, { removeSelection: true }); var copyButtonState = copyButtonPluginKey.getState(state); if (copyButtonState !== null && copyButtonState !== void 0 && copyButtonState.copied) { tr.setMeta(copyButtonPluginKey, { copied: false }); } if (dispatch) { dispatch(tr); } return true; } export var createToolbarCopyCommandForNode = function createToolbarCopyCommandForNode(nodeType, editorAnalyticsApi, api, onClickMessage) { return function (state, dispatch) { var tr = state.tr, schema = state.schema; // This command should only be triggered by the Copy button in the floating toolbar // which is only visible when selection is inside the target node var contentNodeWithPos = getSelectedNodeOrNodeParentByNodeType({ nodeType: nodeType, selection: tr.selection }); if (!contentNodeWithPos) { return false; } var copyToClipboardTr = tr; copyToClipboardTr.setMeta(copyButtonPluginKey, { copied: true }); var analyticsPayload = getAnalyticsPayload(state, ACTION.COPIED); if (analyticsPayload && editorAnalyticsApi) { var _editorAnalyticsApi$a2; analyticsPayload.attributes.inputMethod = INPUT_METHOD.FLOATING_TB; analyticsPayload.attributes.nodeType = contentNodeWithPos.node.type.name; editorAnalyticsApi === null || editorAnalyticsApi === void 0 || (_editorAnalyticsApi$a2 = editorAnalyticsApi.attachAnalyticsEvent) === null || _editorAnalyticsApi$a2 === void 0 || _editorAnalyticsApi$a2.call(editorAnalyticsApi, analyticsPayload)(copyToClipboardTr); } if (dispatch) { // As calling copyHTMLToClipboard causes side effects -- we only run this when // dispatch is provided -- as otherwise the consumer is only testing to see if // the action is availble. var domNode = toDOM(contentNodeWithPos.node, schema); copyDomNode(domNode, contentNodeWithPos.node.type, tr.selection); copyToClipboardTr.setMeta('scrollIntoView', false); dispatch(copyToClipboardTr); } /** * A11y-10275 This prop api drilled from the plugin all the way down to the usage * because it's the safest way to ensure accessibilityUtils is available and not * undefined. * * see thread https://atlassian.slack.com/archives/C02CDUS3SUS/p1744256326459569 */ if (onClickMessage) { var _api$accessibilityUti; api === null || api === void 0 || (_api$accessibilityUti = api.accessibilityUtils) === null || _api$accessibilityUti === void 0 || _api$accessibilityUti.actions.ariaNotify(onClickMessage, { priority: 'important' }); } return true; }; }; export var resetCopiedState = function resetCopiedState(nodeType, hoverDecoration, onMouseLeave) { return function (state, dispatch) { var customTr = state.tr; // Avoid multipe dispatch // https://product-fabric.atlassian.net/wiki/spaces/E/pages/2241659456/All+about+dispatch+and+why+there+shouldn+t+be+multiple#How-do-I-avoid-them%3F var customDispatch = function customDispatch(tr) { customTr = tr; }; onMouseLeave ? onMouseLeave(state, customDispatch) : hoverDecoration === null || hoverDecoration === void 0 ? void 0 : hoverDecoration(nodeType, false)(state, customDispatch); var copyButtonState = copyButtonPluginKey.getState(state); if (copyButtonState !== null && copyButtonState !== void 0 && copyButtonState.copied) { customTr.setMeta(copyButtonPluginKey, { copied: false }); } if (dispatch) { dispatch(customTr); } return true; }; };