@atlaskit/editor-plugin-hyperlink 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/.eslintrc.js +6 -0
  2. package/CHANGELOG.md +1 -0
  3. package/LICENSE.md +13 -0
  4. package/README.md +7 -0
  5. package/dist/cjs/Toolbar.js +280 -0
  6. package/dist/cjs/commands.js +242 -0
  7. package/dist/cjs/index.js +12 -0
  8. package/dist/cjs/plugin.js +122 -0
  9. package/dist/cjs/pm-plugins/fake-curor-for-toolbar-plugin-key.js +9 -0
  10. package/dist/cjs/pm-plugins/fake-cursor-for-toolbar.js +68 -0
  11. package/dist/cjs/pm-plugins/input-rule.js +95 -0
  12. package/dist/cjs/pm-plugins/keymap.js +75 -0
  13. package/dist/cjs/pm-plugins/main.js +257 -0
  14. package/dist/cjs/pm-plugins/toolbar-buttons.js +43 -0
  15. package/dist/cjs/version.json +5 -0
  16. package/dist/es2019/Toolbar.js +260 -0
  17. package/dist/es2019/commands.js +225 -0
  18. package/dist/es2019/index.js +1 -0
  19. package/dist/es2019/plugin.js +106 -0
  20. package/dist/es2019/pm-plugins/fake-curor-for-toolbar-plugin-key.js +2 -0
  21. package/dist/es2019/pm-plugins/fake-cursor-for-toolbar.js +63 -0
  22. package/dist/es2019/pm-plugins/input-rule.js +80 -0
  23. package/dist/es2019/pm-plugins/keymap.js +65 -0
  24. package/dist/es2019/pm-plugins/main.js +261 -0
  25. package/dist/es2019/pm-plugins/toolbar-buttons.js +39 -0
  26. package/dist/es2019/version.json +5 -0
  27. package/dist/esm/Toolbar.js +271 -0
  28. package/dist/esm/commands.js +222 -0
  29. package/dist/esm/index.js +1 -0
  30. package/dist/esm/plugin.js +114 -0
  31. package/dist/esm/pm-plugins/fake-curor-for-toolbar-plugin-key.js +2 -0
  32. package/dist/esm/pm-plugins/fake-cursor-for-toolbar.js +61 -0
  33. package/dist/esm/pm-plugins/input-rule.js +85 -0
  34. package/dist/esm/pm-plugins/keymap.js +67 -0
  35. package/dist/esm/pm-plugins/main.js +248 -0
  36. package/dist/esm/pm-plugins/toolbar-buttons.js +34 -0
  37. package/dist/esm/version.json +5 -0
  38. package/dist/types/Toolbar.d.ts +8 -0
  39. package/dist/types/commands.d.ts +20 -0
  40. package/dist/types/index.d.ts +3 -0
  41. package/dist/types/plugin.d.ts +38 -0
  42. package/dist/types/pm-plugins/fake-curor-for-toolbar-plugin-key.d.ts +2 -0
  43. package/dist/types/pm-plugins/fake-cursor-for-toolbar.d.ts +3 -0
  44. package/dist/types/pm-plugins/input-rule.d.ts +8 -0
  45. package/dist/types/pm-plugins/keymap.d.ts +4 -0
  46. package/dist/types/pm-plugins/main.d.ts +7 -0
  47. package/dist/types/pm-plugins/toolbar-buttons.d.ts +21 -0
  48. package/dist/types-ts4.5/Toolbar.d.ts +8 -0
  49. package/dist/types-ts4.5/commands.d.ts +20 -0
  50. package/dist/types-ts4.5/index.d.ts +3 -0
  51. package/dist/types-ts4.5/plugin.d.ts +38 -0
  52. package/dist/types-ts4.5/pm-plugins/fake-curor-for-toolbar-plugin-key.d.ts +2 -0
  53. package/dist/types-ts4.5/pm-plugins/fake-cursor-for-toolbar.d.ts +3 -0
  54. package/dist/types-ts4.5/pm-plugins/input-rule.d.ts +8 -0
  55. package/dist/types-ts4.5/pm-plugins/keymap.d.ts +4 -0
  56. package/dist/types-ts4.5/pm-plugins/main.d.ts +7 -0
  57. package/dist/types-ts4.5/pm-plugins/toolbar-buttons.d.ts +21 -0
  58. package/package.json +107 -0
  59. package/tmp/api-report-tmp.d.ts +68 -0
@@ -0,0 +1,260 @@
1
+ import React from 'react';
2
+ import { isSafeUrl } from '@atlaskit/adf-schema';
3
+ import { ACTION, ACTION_SUBJECT_ID, buildOpenedSettingsPayload, buildVisitedLinkPayload, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
+ import { commandWithMetadata } from '@atlaskit/editor-common/card';
5
+ import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
6
+ import { HyperlinkAddToolbar } from '@atlaskit/editor-common/link';
7
+ import { linkMessages, linkToolbarMessages as linkToolbarCommonMessages } from '@atlaskit/editor-common/messages';
8
+ import { LINKPICKER_HEIGHT_IN_PX, RECENT_SEARCH_HEIGHT_IN_PX, RECENT_SEARCH_WIDTH_IN_PX } from '@atlaskit/editor-common/ui';
9
+ import { normalizeUrl } from '@atlaskit/editor-common/utils';
10
+ import CogIcon from '@atlaskit/icon/glyph/editor/settings';
11
+ import UnlinkIcon from '@atlaskit/icon/glyph/editor/unlink';
12
+ import OpenIcon from '@atlaskit/icon/glyph/shortcut';
13
+ import { editInsertedLink, insertLinkWithAnalytics, onClickAwayCallback, onEscapeCallback, removeLink, updateLink } from './commands';
14
+ import { stateKey } from './pm-plugins/main';
15
+ import { toolbarKey } from './pm-plugins/toolbar-buttons';
16
+ /* type guard for edit links */
17
+ function isEditLink(linkMark) {
18
+ return linkMark.pos !== undefined;
19
+ }
20
+ const dispatchAnalytics = (dispatch, state, analyticsBuilder, editorAnalyticsApi) => {
21
+ if (dispatch) {
22
+ const {
23
+ tr
24
+ } = state;
25
+ editorAnalyticsApi === null || editorAnalyticsApi === void 0 ? void 0 : editorAnalyticsApi.attachAnalyticsEvent(analyticsBuilder(ACTION_SUBJECT_ID.HYPERLINK))(tr);
26
+ dispatch(tr);
27
+ }
28
+ };
29
+ const visitHyperlink = editorAnalyticsApi => (state, dispatch) => {
30
+ dispatchAnalytics(dispatch, state, buildVisitedLinkPayload, editorAnalyticsApi);
31
+ return true;
32
+ };
33
+ const openLinkSettings = editorAnalyticsApi => (state, dispatch) => {
34
+ dispatchAnalytics(dispatch, state, buildOpenedSettingsPayload, editorAnalyticsApi);
35
+ return true;
36
+ };
37
+ function getLinkText(activeLinkMark, state) {
38
+ if (!activeLinkMark.node) {
39
+ return undefined;
40
+ }
41
+ const textToUrl = normalizeUrl(activeLinkMark.node.text);
42
+ const linkMark = activeLinkMark.node.marks.find(mark => mark.type === state.schema.marks.link);
43
+ const linkHref = linkMark && linkMark.attrs.href;
44
+ if (textToUrl === linkHref) {
45
+ return undefined;
46
+ }
47
+ return activeLinkMark.node.text;
48
+ }
49
+ export function HyperlinkAddToolbarWithState({
50
+ linkPickerOptions = {},
51
+ onSubmit,
52
+ displayText,
53
+ displayUrl,
54
+ providerFactory,
55
+ view,
56
+ onCancel,
57
+ invokeMethod,
58
+ featureFlags,
59
+ onClose,
60
+ onEscapeCallback,
61
+ onClickAwayCallback,
62
+ pluginInjectionApi
63
+ }) {
64
+ const {
65
+ hyperlinkState
66
+ } = useSharedPluginState(pluginInjectionApi, ['hyperlink']);
67
+ return /*#__PURE__*/React.createElement(HyperlinkAddToolbar, {
68
+ linkPickerOptions: linkPickerOptions,
69
+ onSubmit: onSubmit,
70
+ displayText: displayText,
71
+ displayUrl: displayUrl,
72
+ providerFactory: providerFactory,
73
+ view: view,
74
+ onCancel: onCancel,
75
+ invokeMethod: invokeMethod,
76
+ featureFlags: featureFlags,
77
+ onClose: onClose,
78
+ onEscapeCallback: onEscapeCallback,
79
+ onClickAwayCallback: onClickAwayCallback,
80
+ hyperlinkPluginState: hyperlinkState
81
+ });
82
+ }
83
+ const getSettingsButtonGroup = (state, intl, featureFlags, editorAnalyticsApi) => {
84
+ const {
85
+ floatingToolbarLinkSettingsButton
86
+ } = featureFlags;
87
+ return floatingToolbarLinkSettingsButton === 'true' ? [{
88
+ type: 'separator'
89
+ }, {
90
+ id: 'editor.link.settings',
91
+ type: 'button',
92
+ icon: CogIcon,
93
+ title: intl.formatMessage(linkToolbarCommonMessages.settingsLink),
94
+ onClick: openLinkSettings(editorAnalyticsApi),
95
+ href: 'https://id.atlassian.com/manage-profile/link-preferences',
96
+ target: '_blank'
97
+ }] : [];
98
+ };
99
+ export const getToolbarConfig = (options, featureFlags, pluginInjectionApi) => (state, intl, providerFactory) => {
100
+ var _pluginInjectionApi$d;
101
+ const {
102
+ formatMessage
103
+ } = intl;
104
+ const linkState = stateKey.getState(state);
105
+ const editorAnalyticsApi = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$d = pluginInjectionApi.dependencies.analytics) === null || _pluginInjectionApi$d === void 0 ? void 0 : _pluginInjectionApi$d.actions;
106
+
107
+ /**
108
+ * Enable focus trap only if feature flag is enabled AND for the new version of the picker
109
+ */
110
+ const {
111
+ lpLinkPicker,
112
+ lpLinkPickerFocusTrap,
113
+ preventPopupOverflow
114
+ } = featureFlags;
115
+ const shouldEnableFocusTrap = Boolean(lpLinkPicker && lpLinkPickerFocusTrap);
116
+ if (linkState && linkState.activeLinkMark) {
117
+ const {
118
+ activeLinkMark
119
+ } = linkState;
120
+ const hyperLinkToolbar = {
121
+ title: 'Hyperlink floating controls',
122
+ 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),
123
+ // Use only the node types existing in the schema ED-6745
124
+ align: 'left',
125
+ className: activeLinkMark.type.match('INSERT|EDIT_INSERTED') ? 'hyperlink-floating-toolbar' : ''
126
+ };
127
+ switch (activeLinkMark.type) {
128
+ case 'EDIT':
129
+ {
130
+ var _toolbarKey$getState$, _toolbarKey$getState;
131
+ const {
132
+ pos,
133
+ node
134
+ } = activeLinkMark;
135
+ const linkMark = node.marks.filter(mark => mark.type === state.schema.marks.link);
136
+ const link = linkMark[0] && linkMark[0].attrs.href;
137
+ const isValidUrl = isSafeUrl(link);
138
+ const labelOpenLink = formatMessage(isValidUrl ? linkMessages.openLink : linkToolbarCommonMessages.unableToOpenLink);
139
+ // TODO: ED-14403 investigate why these are not translating?
140
+ const labelUnlink = formatMessage(linkToolbarCommonMessages.unlink);
141
+ const editLink = formatMessage(linkToolbarCommonMessages.editLink);
142
+ let metadata = {
143
+ url: link,
144
+ title: ''
145
+ };
146
+ if (activeLinkMark.node.text) {
147
+ metadata.title = activeLinkMark.node.text;
148
+ }
149
+ return {
150
+ ...hyperLinkToolbar,
151
+ height: 32,
152
+ width: 250,
153
+ items: [...((_toolbarKey$getState$ = (_toolbarKey$getState = toolbarKey.getState(state)) === null || _toolbarKey$getState === void 0 ? void 0 : _toolbarKey$getState.items(state, intl, providerFactory, link)) !== null && _toolbarKey$getState$ !== void 0 ? _toolbarKey$getState$ : []), {
154
+ id: 'editor.link.edit',
155
+ type: 'button',
156
+ onClick: editInsertedLink(editorAnalyticsApi),
157
+ selected: false,
158
+ title: editLink,
159
+ showTitle: true,
160
+ metadata: metadata
161
+ }, {
162
+ type: 'separator'
163
+ }, {
164
+ id: 'editor.link.openLink',
165
+ type: 'button',
166
+ disabled: !isValidUrl,
167
+ target: '_blank',
168
+ href: isValidUrl ? link : undefined,
169
+ onClick: visitHyperlink(editorAnalyticsApi),
170
+ selected: false,
171
+ title: labelOpenLink,
172
+ icon: OpenIcon,
173
+ className: 'hyperlink-open-link',
174
+ metadata: metadata,
175
+ tabIndex: null
176
+ }, {
177
+ type: 'separator'
178
+ }, {
179
+ id: 'editor.link.unlink',
180
+ type: 'button',
181
+ onClick: commandWithMetadata(removeLink(pos, editorAnalyticsApi), {
182
+ inputMethod: INPUT_METHOD.FLOATING_TB
183
+ }),
184
+ selected: false,
185
+ title: labelUnlink,
186
+ icon: UnlinkIcon,
187
+ tabIndex: null
188
+ }, {
189
+ type: 'copy-button',
190
+ items: [{
191
+ type: 'separator'
192
+ }, {
193
+ state,
194
+ formatMessage: formatMessage,
195
+ markType: state.schema.marks.link
196
+ }]
197
+ }, ...getSettingsButtonGroup(state, intl, featureFlags, editorAnalyticsApi)],
198
+ scrollable: true
199
+ };
200
+ }
201
+ case 'EDIT_INSERTED':
202
+ case 'INSERT':
203
+ {
204
+ let link;
205
+ if (isEditLink(activeLinkMark) && activeLinkMark.node) {
206
+ const linkMark = activeLinkMark.node.marks.filter(mark => mark.type === state.schema.marks.link);
207
+ link = linkMark[0] && linkMark[0].attrs.href;
208
+ }
209
+ const displayText = isEditLink(activeLinkMark) ? getLinkText(activeLinkMark, state) : linkState.activeText;
210
+ const popupHeight = lpLinkPicker ? LINKPICKER_HEIGHT_IN_PX : RECENT_SEARCH_HEIGHT_IN_PX;
211
+ return {
212
+ ...hyperLinkToolbar,
213
+ preventPopupOverflow,
214
+ height: popupHeight,
215
+ width: RECENT_SEARCH_WIDTH_IN_PX,
216
+ focusTrap: shouldEnableFocusTrap,
217
+ items: [{
218
+ type: 'custom',
219
+ fallback: [],
220
+ disableArrowNavigation: true,
221
+ render: (view, idx) => {
222
+ if (!view) {
223
+ return null;
224
+ }
225
+ return /*#__PURE__*/React.createElement(HyperlinkAddToolbarWithState, {
226
+ pluginInjectionApi: pluginInjectionApi,
227
+ view: view,
228
+ key: idx,
229
+ linkPickerOptions: options === null || options === void 0 ? void 0 : options.linkPicker,
230
+ featureFlags: featureFlags,
231
+ displayUrl: link,
232
+ displayText: displayText || '',
233
+ providerFactory: providerFactory,
234
+ onCancel: () => view.focus(),
235
+ onClose: lpLinkPickerFocusTrap ? () => view.focus() : undefined,
236
+ onEscapeCallback: onEscapeCallback,
237
+ onClickAwayCallback: onClickAwayCallback,
238
+ onSubmit: (href, title = '', displayText, inputMethod, analytic) => {
239
+ var _options$cardOptions;
240
+ const isEdit = isEditLink(activeLinkMark);
241
+ const action = isEdit ? ACTION.UPDATED : ACTION.INSERTED;
242
+ const command = isEdit ? commandWithMetadata(updateLink(href, displayText || title, activeLinkMark.pos), {
243
+ action,
244
+ inputMethod,
245
+ sourceEvent: analytic
246
+ }) : insertLinkWithAnalytics(inputMethod, activeLinkMark.from, activeLinkMark.to, href, editorAnalyticsApi, title, displayText, !!(options !== null && options !== void 0 && (_options$cardOptions = options.cardOptions) !== null && _options$cardOptions !== void 0 && _options$cardOptions.provider), analytic);
247
+ command(view.state, view.dispatch, view);
248
+ if (!lpLinkPickerFocusTrap) {
249
+ view.focus();
250
+ }
251
+ }
252
+ });
253
+ }
254
+ }]
255
+ };
256
+ }
257
+ }
258
+ }
259
+ return;
260
+ };
@@ -0,0 +1,225 @@
1
+ import { Selection } from 'prosemirror-state';
2
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, buildEditLinkPayload, EVENT_TYPE, INPUT_METHOD, unlinkPayload } from '@atlaskit/editor-common/analytics';
3
+ import { addLinkMetadata, commandWithMetadata } from '@atlaskit/editor-common/card';
4
+ import { withAnalytics } from '@atlaskit/editor-common/editor-analytics';
5
+ import { isTextAtPos, LinkAction } from '@atlaskit/editor-common/link';
6
+ import { filterCommands as filter, getLinkCreationAnalyticsEvent, normalizeUrl } from '@atlaskit/editor-common/utils';
7
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
8
+ import { stateKey } from './pm-plugins/main';
9
+ import { toolbarKey } from './pm-plugins/toolbar-buttons';
10
+ export function setLinkHref(href, pos, editorAnalyticsApi, to, isTabPressed) {
11
+ return filter(isTextAtPos(pos), (state, dispatch) => {
12
+ const $pos = state.doc.resolve(pos);
13
+ const node = state.doc.nodeAt(pos);
14
+ const linkMark = state.schema.marks.link;
15
+ const mark = linkMark.isInSet(node.marks);
16
+ const url = normalizeUrl(href);
17
+ if (mark && mark.attrs.href === url) {
18
+ return false;
19
+ }
20
+ const rightBound = to && pos !== to ? to : pos - $pos.textOffset + node.nodeSize;
21
+ const tr = state.tr.removeMark(pos, rightBound, linkMark);
22
+ if (href.trim()) {
23
+ tr.addMark(pos, rightBound, linkMark.create({
24
+ ...(mark && mark.attrs || {}),
25
+ href: url
26
+ }));
27
+ } else {
28
+ editorAnalyticsApi === null || editorAnalyticsApi === void 0 ? void 0 : editorAnalyticsApi.attachAnalyticsEvent(unlinkPayload(ACTION_SUBJECT_ID.HYPERLINK))(tr);
29
+ }
30
+ if (!isTabPressed) {
31
+ tr.setMeta(stateKey, {
32
+ type: LinkAction.HIDE_TOOLBAR
33
+ });
34
+ }
35
+ if (dispatch) {
36
+ dispatch(tr);
37
+ }
38
+ return true;
39
+ });
40
+ }
41
+ export function updateLink(href, text, pos, to) {
42
+ return (state, dispatch) => {
43
+ const $pos = state.doc.resolve(pos);
44
+ const node = state.doc.nodeAt(pos);
45
+ if (!node) {
46
+ return false;
47
+ }
48
+ const url = normalizeUrl(href);
49
+ const mark = state.schema.marks.link.isInSet(node.marks);
50
+ const linkMark = state.schema.marks.link;
51
+ const rightBound = to && pos !== to ? to : pos - $pos.textOffset + node.nodeSize;
52
+ const tr = state.tr;
53
+ if (!url && text) {
54
+ tr.removeMark(pos, rightBound, linkMark);
55
+ tr.insertText(text, pos, rightBound);
56
+ } else if (!url) {
57
+ return false;
58
+ } else {
59
+ tr.insertText(text, pos, rightBound);
60
+ // Casting to LinkAttributes to prevent wrong attributes been passed (Example ED-7951)
61
+ const linkAttrs = {
62
+ ...(mark && mark.attrs || {}),
63
+ href: url
64
+ };
65
+ tr.addMark(pos, pos + text.length, linkMark.create(linkAttrs));
66
+ tr.setMeta(stateKey, {
67
+ type: LinkAction.HIDE_TOOLBAR
68
+ });
69
+ }
70
+ if (dispatch) {
71
+ dispatch(tr);
72
+ }
73
+ return true;
74
+ };
75
+ }
76
+ export function insertLink(from, to, incomingHref, incomingTitle, displayText, source, sourceEvent) {
77
+ return (state, dispatch) => {
78
+ const link = state.schema.marks.link;
79
+ const {
80
+ tr
81
+ } = state;
82
+ if (incomingHref.trim()) {
83
+ var _stateKey$getState;
84
+ const normalizedUrl = normalizeUrl(incomingHref);
85
+ // NB: in this context, `currentText` represents text which has been
86
+ // highlighted in the Editor, upon which a link is is being added.
87
+ const currentText = (_stateKey$getState = stateKey.getState(state)) === null || _stateKey$getState === void 0 ? void 0 : _stateKey$getState.activeText;
88
+ let markEnd = to;
89
+ const text = displayText || incomingTitle || incomingHref;
90
+ if (!displayText || displayText !== currentText) {
91
+ tr.insertText(text, from, to);
92
+ if (!isTextAtPos(from)(state)) {
93
+ markEnd = from + text.length + 1;
94
+ } else {
95
+ markEnd = from + text.length;
96
+ }
97
+ }
98
+ tr.addMark(from, markEnd, link.create({
99
+ href: normalizedUrl
100
+ }));
101
+ tr.setSelection(Selection.near(tr.doc.resolve(markEnd)));
102
+ if (!displayText || displayText === incomingHref) {
103
+ var _toolbarKey$getState;
104
+ const queueCardsFromChangedTr = (_toolbarKey$getState = toolbarKey.getState(state)) === null || _toolbarKey$getState === void 0 ? void 0 : _toolbarKey$getState.onInsertLinkCallback;
105
+ if (queueCardsFromChangedTr) {
106
+ queueCardsFromChangedTr === null || queueCardsFromChangedTr === void 0 ? void 0 : queueCardsFromChangedTr(state, tr, source, ACTION.INSERTED, false, sourceEvent);
107
+ } else {
108
+ addLinkMetadata(state.selection, tr, {
109
+ action: ACTION.INSERTED,
110
+ inputMethod: source,
111
+ sourceEvent
112
+ });
113
+ }
114
+ } else if (getBooleanFF('platform.linking-platform.editor.fix-link-insert-analytics')) {
115
+ /**
116
+ * Add link metadata because queue cards would have otherwise handled this for us
117
+ */
118
+ addLinkMetadata(state.selection, tr, {
119
+ action: ACTION.INSERTED,
120
+ inputMethod: source,
121
+ sourceEvent
122
+ });
123
+ }
124
+ tr.setMeta(stateKey, {
125
+ type: LinkAction.HIDE_TOOLBAR
126
+ });
127
+ if (dispatch) {
128
+ dispatch(tr);
129
+ }
130
+ return true;
131
+ }
132
+ tr.setMeta(stateKey, {
133
+ type: LinkAction.HIDE_TOOLBAR
134
+ });
135
+ if (dispatch) {
136
+ dispatch(tr);
137
+ }
138
+ return false;
139
+ };
140
+ }
141
+ export const insertLinkWithAnalytics = (inputMethod, from, to, href, editorAnalyticsApi, title, displayText, cardsAvailable = false, sourceEvent = undefined) => {
142
+ // If smart cards are available, we send analytics for hyperlinks when a smart link is rejected.
143
+ if (cardsAvailable && !title && !displayText) {
144
+ return insertLink(from, to, href, title, displayText, inputMethod, sourceEvent);
145
+ }
146
+ return withAnalytics(editorAnalyticsApi, getLinkCreationAnalyticsEvent(inputMethod, href))(insertLink(from, to, href, title, displayText, inputMethod, sourceEvent));
147
+ };
148
+ export const insertLinkWithAnalyticsMobileNative = (inputMethod, from, to, href, editorAnalyticsApi, title, displayText) => {
149
+ return withAnalytics(editorAnalyticsApi, getLinkCreationAnalyticsEvent(inputMethod, href))(insertLink(from, to, href, title, displayText, inputMethod));
150
+ };
151
+ export function removeLink(pos, editorAnalyticsApi) {
152
+ return commandWithMetadata(setLinkHref('', pos, editorAnalyticsApi), {
153
+ action: ACTION.UNLINK
154
+ });
155
+ }
156
+ export function editInsertedLink(editorAnalyticsApi) {
157
+ return (state, dispatch) => {
158
+ if (dispatch) {
159
+ const {
160
+ tr
161
+ } = state;
162
+ tr.setMeta(stateKey, {
163
+ type: LinkAction.EDIT_INSERTED_TOOLBAR,
164
+ inputMethod: INPUT_METHOD.FLOATING_TB
165
+ });
166
+ editorAnalyticsApi === null || editorAnalyticsApi === void 0 ? void 0 : editorAnalyticsApi.attachAnalyticsEvent(buildEditLinkPayload(ACTION_SUBJECT_ID.HYPERLINK))(tr);
167
+ dispatch(tr);
168
+ }
169
+ return true;
170
+ };
171
+ }
172
+ export function showLinkToolbar(inputMethod, editorAnalyticsApi) {
173
+ return function (state, dispatch) {
174
+ if (dispatch) {
175
+ const tr = state.tr.setMeta(stateKey, {
176
+ type: LinkAction.SHOW_INSERT_TOOLBAR,
177
+ inputMethod
178
+ });
179
+ editorAnalyticsApi === null || editorAnalyticsApi === void 0 ? void 0 : editorAnalyticsApi.attachAnalyticsEvent({
180
+ action: ACTION.INVOKED,
181
+ actionSubject: ACTION_SUBJECT.TYPEAHEAD,
182
+ actionSubjectId: ACTION_SUBJECT_ID.TYPEAHEAD_LINK,
183
+ attributes: {
184
+ inputMethod
185
+ },
186
+ eventType: EVENT_TYPE.UI
187
+ })(tr);
188
+ dispatch(tr);
189
+ }
190
+ return true;
191
+ };
192
+ }
193
+ export function hideLinkToolbar() {
194
+ return function (state, dispatch) {
195
+ if (dispatch) {
196
+ dispatch(hideLinkToolbarSetMeta(state.tr));
197
+ }
198
+ return true;
199
+ };
200
+ }
201
+ export const hideLinkToolbarSetMeta = tr => {
202
+ return tr.setMeta(stateKey, {
203
+ type: LinkAction.HIDE_TOOLBAR
204
+ });
205
+ };
206
+ export const onEscapeCallback = (state, dispatch) => {
207
+ var _toolbarKey$getState2, _toolbarKey$getState3;
208
+ const {
209
+ tr
210
+ } = state;
211
+ hideLinkToolbarSetMeta(tr);
212
+ (_toolbarKey$getState2 = toolbarKey.getState(state)) === null || _toolbarKey$getState2 === void 0 ? void 0 : (_toolbarKey$getState3 = _toolbarKey$getState2.onEscapeCallback) === null || _toolbarKey$getState3 === void 0 ? void 0 : _toolbarKey$getState3.call(_toolbarKey$getState2, tr);
213
+ if (dispatch) {
214
+ dispatch(tr);
215
+ return true;
216
+ }
217
+ return false;
218
+ };
219
+ export const onClickAwayCallback = (state, dispatch) => {
220
+ if (dispatch) {
221
+ hideLinkToolbar()(state, dispatch);
222
+ return true;
223
+ }
224
+ return false;
225
+ };
@@ -0,0 +1 @@
1
+ export { hyperlinkPlugin } from './plugin';
@@ -0,0 +1,106 @@
1
+ import React from 'react';
2
+ import { link } from '@atlaskit/adf-schema';
3
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
+ import { addLink, tooltip } from '@atlaskit/editor-common/keymaps';
5
+ import { LinkAction } from '@atlaskit/editor-common/link';
6
+ import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
7
+ import { IconLink } from '@atlaskit/editor-common/quick-insert';
8
+ import { hideLinkToolbarSetMeta, showLinkToolbar } from './commands';
9
+ import fakeCursorToolbarPlugin from './pm-plugins/fake-cursor-for-toolbar';
10
+ import { createInputRulePlugin } from './pm-plugins/input-rule';
11
+ import { createKeymapPlugin } from './pm-plugins/keymap';
12
+ import { plugin, stateKey } from './pm-plugins/main';
13
+ import { prependToolbarButtons, toolbarButtonsPlugin } from './pm-plugins/toolbar-buttons';
14
+ import { getToolbarConfig } from './Toolbar';
15
+ export const hyperlinkPlugin = (options = {}, api) => {
16
+ var _api$dependencies, _api$dependencies$fea;
17
+ const featureFlags = (api === null || api === void 0 ? void 0 : (_api$dependencies = api.dependencies) === null || _api$dependencies === void 0 ? void 0 : (_api$dependencies$fea = _api$dependencies.featureFlags) === null || _api$dependencies$fea === void 0 ? void 0 : _api$dependencies$fea.sharedState.currentState()) || {};
18
+ return {
19
+ name: 'hyperlink',
20
+ marks() {
21
+ return [{
22
+ name: 'link',
23
+ mark: link
24
+ }];
25
+ },
26
+ actions: {
27
+ prependToolbarButtons,
28
+ showLinkToolbar: (inputMethod = INPUT_METHOD.TOOLBAR) => {
29
+ var _api$dependencies$ana;
30
+ return showLinkToolbar(inputMethod, api === null || api === void 0 ? void 0 : (_api$dependencies$ana = api.dependencies.analytics) === null || _api$dependencies$ana === void 0 ? void 0 : _api$dependencies$ana.actions);
31
+ },
32
+ hideLinkToolbar: hideLinkToolbarSetMeta
33
+ },
34
+ getSharedState(editorState) {
35
+ if (!editorState) {
36
+ return undefined;
37
+ }
38
+ return stateKey.getState(editorState);
39
+ },
40
+ pmPlugins() {
41
+ var _options$cardOptions;
42
+ // Skip analytics if card provider is available, as they will be
43
+ // sent on handleRejected upon attempting to resolve smart link.
44
+ const skipAnalytics = !!(options !== null && options !== void 0 && (_options$cardOptions = options.cardOptions) !== null && _options$cardOptions !== void 0 && _options$cardOptions.provider);
45
+ return [{
46
+ name: 'hyperlink',
47
+ plugin: ({
48
+ dispatch
49
+ }) => plugin(dispatch, options === null || options === void 0 ? void 0 : options.editorAppearance)
50
+ }, {
51
+ name: 'fakeCursorToolbarPlugin',
52
+ plugin: () => fakeCursorToolbarPlugin
53
+ }, {
54
+ name: 'hyperlinkInputRule',
55
+ plugin: ({
56
+ schema,
57
+ featureFlags
58
+ }) => {
59
+ var _api$dependencies$ana2;
60
+ return createInputRulePlugin(schema, skipAnalytics, featureFlags, api === null || api === void 0 ? void 0 : (_api$dependencies$ana2 = api.dependencies.analytics) === null || _api$dependencies$ana2 === void 0 ? void 0 : _api$dependencies$ana2.actions);
61
+ }
62
+ }, {
63
+ name: 'hyperlinkKeymap',
64
+ plugin: () => {
65
+ var _api$dependencies$ana3;
66
+ return createKeymapPlugin(skipAnalytics, api === null || api === void 0 ? void 0 : (_api$dependencies$ana3 = api.dependencies.analytics) === null || _api$dependencies$ana3 === void 0 ? void 0 : _api$dependencies$ana3.actions);
67
+ }
68
+ }, {
69
+ name: 'hyperlinkToolbarButtons',
70
+ plugin: toolbarButtonsPlugin
71
+ }];
72
+ },
73
+ pluginsOptions: {
74
+ quickInsert: ({
75
+ formatMessage
76
+ }) => [{
77
+ id: 'hyperlink',
78
+ title: formatMessage(messages.link),
79
+ description: formatMessage(messages.linkDescription),
80
+ keywords: ['hyperlink', 'url'],
81
+ priority: 1200,
82
+ keyshortcut: tooltip(addLink),
83
+ icon: () => /*#__PURE__*/React.createElement(IconLink, null),
84
+ action(insert, state) {
85
+ var _api$dependencies2, _api$dependencies2$an, _api$dependencies2$an2, _api$dependencies2$an3;
86
+ const tr = insert(undefined);
87
+ tr.setMeta(stateKey, {
88
+ type: LinkAction.SHOW_INSERT_TOOLBAR,
89
+ inputMethod: INPUT_METHOD.QUICK_INSERT
90
+ });
91
+ const analyticsAttached = api === null || api === void 0 ? void 0 : (_api$dependencies2 = api.dependencies) === null || _api$dependencies2 === void 0 ? void 0 : (_api$dependencies2$an = _api$dependencies2.analytics) === null || _api$dependencies2$an === void 0 ? void 0 : (_api$dependencies2$an2 = _api$dependencies2$an.actions) === null || _api$dependencies2$an2 === void 0 ? void 0 : (_api$dependencies2$an3 = _api$dependencies2$an2.attachAnalyticsEvent) === null || _api$dependencies2$an3 === void 0 ? void 0 : _api$dependencies2$an3.call(_api$dependencies2$an2, {
92
+ action: ACTION.INVOKED,
93
+ actionSubject: ACTION_SUBJECT.TYPEAHEAD,
94
+ actionSubjectId: ACTION_SUBJECT_ID.TYPEAHEAD_LINK,
95
+ attributes: {
96
+ inputMethod: INPUT_METHOD.QUICK_INSERT
97
+ },
98
+ eventType: EVENT_TYPE.UI
99
+ })(tr);
100
+ return analyticsAttached !== false ? tr : false;
101
+ }
102
+ }],
103
+ floatingToolbar: getToolbarConfig(options, featureFlags, api)
104
+ }
105
+ };
106
+ };
@@ -0,0 +1,2 @@
1
+ import { PluginKey } from 'prosemirror-state';
2
+ export const fakeCursorForToolbarPluginKey = new PluginKey('fakeCursorToolbarPlugin');
@@ -0,0 +1,63 @@
1
+ import { Decoration, DecorationSet } from 'prosemirror-view';
2
+ import { InsertStatus } from '@atlaskit/editor-common/link';
3
+ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
4
+ import { fakeCursorForToolbarPluginKey } from './fake-curor-for-toolbar-plugin-key';
5
+ import { stateKey as hyperlinkStateKey } from './main';
6
+ const createTextCursor = pos => {
7
+ const node = document.createElement('div');
8
+ node.className = 'ProseMirror-fake-text-cursor';
9
+ return Decoration.widget(pos, node, {
10
+ key: 'hyperlink-text-cursor'
11
+ });
12
+ };
13
+ const createTextSelection = (from, to) => Decoration.inline(from, to, {
14
+ class: 'ProseMirror-fake-text-selection'
15
+ });
16
+ const getInsertLinkToolbarState = editorState => {
17
+ const state = hyperlinkStateKey.getState(editorState);
18
+ if (state && state.activeLinkMark) {
19
+ if (state.activeLinkMark.type === InsertStatus.INSERT_LINK_TOOLBAR) {
20
+ return state.activeLinkMark;
21
+ }
22
+ }
23
+ return undefined;
24
+ };
25
+ const fakeCursorToolbarPlugin = new SafePlugin({
26
+ key: fakeCursorForToolbarPluginKey,
27
+ state: {
28
+ init() {
29
+ return DecorationSet.empty;
30
+ },
31
+ apply(tr, pluginState, oldState, newState) {
32
+ const oldInsertToolbarState = getInsertLinkToolbarState(oldState);
33
+ const insertToolbarState = getInsertLinkToolbarState(newState);
34
+ // Map DecorationSet if it still refers to the same position in the document
35
+ if (oldInsertToolbarState && insertToolbarState) {
36
+ const {
37
+ from,
38
+ to
39
+ } = insertToolbarState;
40
+ const oldFrom = tr.mapping.map(oldInsertToolbarState.from);
41
+ const oldTo = tr.mapping.map(oldInsertToolbarState.to);
42
+ if (oldFrom === from && oldTo === to) {
43
+ return pluginState.map(tr.mapping, tr.doc);
44
+ }
45
+ }
46
+ // Update DecorationSet if new insert toolbar, or if we have moved to a different position in the doc
47
+ if (insertToolbarState) {
48
+ const {
49
+ from,
50
+ to
51
+ } = insertToolbarState;
52
+ return DecorationSet.create(tr.doc, [from === to ? createTextCursor(from) : createTextSelection(from, to)]);
53
+ }
54
+ return DecorationSet.empty;
55
+ }
56
+ },
57
+ props: {
58
+ decorations(state) {
59
+ return fakeCursorForToolbarPluginKey.getState(state);
60
+ }
61
+ }
62
+ });
63
+ export default fakeCursorToolbarPlugin;