@atlaskit/editor-plugin-hyperlink 1.3.1 → 1.5.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @atlaskit/editor-plugin-hyperlink
2
2
 
3
+ ## 1.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#89386](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/89386) [`567378286049`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/567378286049) - Adds `addToolbarItems` action to hyperlink toolbar plugin in order to support adding addition toolbar items to the end of the floating toolbar for blue links. Requires platform.editor.card.inject-settings-button platform feature flag for usage.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+
13
+ ## 1.4.0
14
+
15
+ ### Minor Changes
16
+
17
+ - [#84430](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/84430) [`2981b2835973`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/2981b2835973) - [ux] EDM-9111 Prevent linkification of links with suspicious tlds
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies
22
+
3
23
  ## 1.3.1
4
24
 
5
25
  ### Patch Changes
@@ -5,9 +5,9 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.HyperlinkAddToolbarWithState = HyperlinkAddToolbarWithState;
8
- exports.getToolbarConfig = void 0;
9
- var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
8
+ exports.mergeAddedItems = exports.getToolbarConfig = void 0;
10
9
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
11
11
  var _react = _interopRequireDefault(require("react"));
12
12
  var _adfSchema = require("@atlaskit/adf-schema");
13
13
  var _analytics = require("@atlaskit/editor-common/analytics");
@@ -20,6 +20,7 @@ var _utils = require("@atlaskit/editor-common/utils");
20
20
  var _settings = _interopRequireDefault(require("@atlaskit/icon/glyph/editor/settings"));
21
21
  var _unlink = _interopRequireDefault(require("@atlaskit/icon/glyph/editor/unlink"));
22
22
  var _shortcut = _interopRequireDefault(require("@atlaskit/icon/glyph/shortcut"));
23
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
23
24
  var _commands = require("./commands");
24
25
  var _main = require("./pm-plugins/main");
25
26
  var _toolbarButtons = require("./pm-plugins/toolbar-buttons");
@@ -108,6 +109,26 @@ var getSettingsButtonGroup = function getSettingsButtonGroup(intl, editorAnalyti
108
109
  target: '_blank'
109
110
  }];
110
111
  };
112
+ var mergeAddedItems = exports.mergeAddedItems = function mergeAddedItems(link) {
113
+ for (var _len = arguments.length, handlerParams = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
114
+ handlerParams[_key - 1] = arguments[_key];
115
+ }
116
+ return function (items, toolbarItemsState) {
117
+ var positions = toolbarItemsState === null || toolbarItemsState === void 0 ? void 0 : toolbarItemsState.items_next;
118
+ if (!positions) {
119
+ return items;
120
+ }
121
+ var start = positions.start;
122
+ var end = positions.end;
123
+ var reduceItems = function reduceItems() {
124
+ var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
125
+ return items.reduce(function (acc, fn) {
126
+ return [].concat((0, _toConsumableArray2.default)(acc), (0, _toConsumableArray2.default)(fn.apply(void 0, handlerParams.concat([link]))));
127
+ }, []);
128
+ };
129
+ return [].concat((0, _toConsumableArray2.default)(reduceItems(start)), (0, _toConsumableArray2.default)(items), (0, _toConsumableArray2.default)(reduceItems(end)));
130
+ };
131
+ };
111
132
  var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(options, pluginInjectionApi) {
112
133
  return function (state, intl, providerFactory) {
113
134
  var _pluginInjectionApi$a, _options$lpLinkPicker;
@@ -151,54 +172,56 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(opti
151
172
  if (activeLinkMark.node.text) {
152
173
  metadata.title = activeLinkMark.node.text;
153
174
  }
175
+ var baseItems = [].concat((0, _toConsumableArray2.default)((_toolbarKey$getState$ = (_toolbarKey$getState = _toolbarButtons.toolbarKey.getState(state)) === null || _toolbarKey$getState === void 0 ? void 0 : _toolbarKey$getState.items(state, intl, providerFactory, link)) !== null && _toolbarKey$getState$ !== void 0 ? _toolbarKey$getState$ : []), [{
176
+ id: 'editor.link.edit',
177
+ testId: 'editor.link.edit',
178
+ type: 'button',
179
+ onClick: (0, _commands.editInsertedLink)(editorAnalyticsApi),
180
+ title: editLink,
181
+ showTitle: true,
182
+ metadata: metadata
183
+ }, {
184
+ type: 'separator'
185
+ }, {
186
+ id: 'editor.link.openLink',
187
+ testId: 'editor.link.openLink',
188
+ type: 'button',
189
+ disabled: !isValidUrl,
190
+ target: '_blank',
191
+ href: isValidUrl ? link : undefined,
192
+ onClick: visitHyperlink(editorAnalyticsApi),
193
+ title: labelOpenLink,
194
+ icon: _shortcut.default,
195
+ className: 'hyperlink-open-link',
196
+ metadata: metadata,
197
+ tabIndex: null
198
+ }, {
199
+ type: 'separator'
200
+ }, {
201
+ id: 'editor.link.unlink',
202
+ testId: 'editor.link.unlink',
203
+ type: 'button',
204
+ onClick: (0, _card.commandWithMetadata)((0, _commands.removeLink)(pos, editorAnalyticsApi), {
205
+ inputMethod: _analytics.INPUT_METHOD.FLOATING_TB
206
+ }),
207
+ title: labelUnlink,
208
+ icon: _unlink.default,
209
+ tabIndex: null
210
+ }, {
211
+ type: 'copy-button',
212
+ items: [{
213
+ type: 'separator'
214
+ }, {
215
+ state: state,
216
+ formatMessage: formatMessage,
217
+ markType: state.schema.marks.link
218
+ }]
219
+ }], (0, _toConsumableArray2.default)((0, _platformFeatureFlags.getBooleanFF)('platform.editor.card.inject-settings-button') ? [] : getSettingsButtonGroup(intl, editorAnalyticsApi)));
220
+ var items = (0, _platformFeatureFlags.getBooleanFF)('platform.editor.card.inject-settings-button') ? mergeAddedItems(link, state, intl, providerFactory)(baseItems, _toolbarButtons.toolbarKey.getState(state)) : baseItems;
154
221
  return _objectSpread(_objectSpread({}, hyperLinkToolbar), {}, {
155
222
  height: 32,
156
223
  width: 250,
157
- items: [].concat((0, _toConsumableArray2.default)((_toolbarKey$getState$ = (_toolbarKey$getState = _toolbarButtons.toolbarKey.getState(state)) === null || _toolbarKey$getState === void 0 ? void 0 : _toolbarKey$getState.items(state, intl, providerFactory, link)) !== null && _toolbarKey$getState$ !== void 0 ? _toolbarKey$getState$ : []), [{
158
- id: 'editor.link.edit',
159
- testId: 'editor.link.edit',
160
- type: 'button',
161
- onClick: (0, _commands.editInsertedLink)(editorAnalyticsApi),
162
- title: editLink,
163
- showTitle: true,
164
- metadata: metadata
165
- }, {
166
- type: 'separator'
167
- }, {
168
- id: 'editor.link.openLink',
169
- testId: 'editor.link.openLink',
170
- type: 'button',
171
- disabled: !isValidUrl,
172
- target: '_blank',
173
- href: isValidUrl ? link : undefined,
174
- onClick: visitHyperlink(editorAnalyticsApi),
175
- title: labelOpenLink,
176
- icon: _shortcut.default,
177
- className: 'hyperlink-open-link',
178
- metadata: metadata,
179
- tabIndex: null
180
- }, {
181
- type: 'separator'
182
- }, {
183
- id: 'editor.link.unlink',
184
- testId: 'editor.link.unlink',
185
- type: 'button',
186
- onClick: (0, _card.commandWithMetadata)((0, _commands.removeLink)(pos, editorAnalyticsApi), {
187
- inputMethod: _analytics.INPUT_METHOD.FLOATING_TB
188
- }),
189
- title: labelUnlink,
190
- icon: _unlink.default,
191
- tabIndex: null
192
- }, {
193
- type: 'copy-button',
194
- items: [{
195
- type: 'separator'
196
- }, {
197
- state: state,
198
- formatMessage: formatMessage,
199
- markType: state.schema.marks.link
200
- }]
201
- }], (0, _toConsumableArray2.default)(getSettingsButtonGroup(intl, editorAnalyticsApi))),
224
+ items: items,
202
225
  scrollable: true
203
226
  });
204
227
  }
@@ -44,6 +44,7 @@ var hyperlinkPlugin = exports.hyperlinkPlugin = function hyperlinkPlugin(_ref) {
44
44
  },
45
45
  actions: {
46
46
  prependToolbarButtons: _toolbarButtons.prependToolbarButtons,
47
+ addToolbarItems: _toolbarButtons.addToolbarItems,
47
48
  hideLinkToolbar: _commands.hideLinkToolbarSetMeta,
48
49
  insertLink: function insertLink(inputMethod, from, to, href, title, displayText) {
49
50
  var _api$analytics2;
@@ -12,8 +12,12 @@ var _analytics = require("@atlaskit/editor-common/analytics");
12
12
  var _card = require("@atlaskit/editor-common/card");
13
13
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
14
14
  var _utils = require("@atlaskit/editor-common/utils");
15
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
15
16
  var _prosemirrorInputRules = require("@atlaskit/prosemirror-input-rules");
16
17
  var _toolbarButtons = require("./toolbar-buttons");
18
+ /**
19
+ * Called when space after link, but not on enter
20
+ */
17
21
  function createLinkInputRule(regexp, editorAnalyticsApi) {
18
22
  // Plain typed text (eg, typing 'www.google.com') should convert to a hyperlink
19
23
  return (0, _utils.createRule)(regexp, function (state, match, start, end) {
@@ -23,7 +27,27 @@ function createLinkInputRule(regexp, editorAnalyticsApi) {
23
27
  return null;
24
28
  }
25
29
  var link = match;
26
- var url = (0, _utils.normalizeUrl)(link.url);
30
+ var url;
31
+ if ((0, _platformFeatureFlags.getBooleanFF)('platform.linking-platform.prevent-suspicious-linkification')) {
32
+ // Property 'url' does not exist on type 'RegExpExecArray', the type of `match`.
33
+ // This check is in case the match is not a Linkify match, which has a url property.
34
+ if (link.url === undefined) {
35
+ return null;
36
+ }
37
+ if (!(0, _utils.shouldAutoLinkifyMatch)(link)) {
38
+ return null;
39
+ }
40
+ url = (0, _utils.normalizeUrl)(link.url);
41
+
42
+ // Not previously handled; don't create a link if the URL is empty.
43
+ // This will only happen if the `regexp` matches more links than the normalizeUrl validation;
44
+ // if they both use the same linkify instance this shouldn't happen.
45
+ if (url === '') {
46
+ return null;
47
+ }
48
+ } else {
49
+ url = (0, _utils.normalizeUrl)(link.url);
50
+ }
27
51
  var markType = schema.mark('link', {
28
52
  href: url
29
53
  });
@@ -74,6 +98,10 @@ function createInputRulePlugin(schema, editorAnalyticsApi) {
74
98
  prefix = _match[1],
75
99
  linkText = _match[2],
76
100
  linkUrl = _match[3];
101
+
102
+ // We don't filter this match here by shouldAutoLinkifyMatch
103
+ // because the intent of creating a link is clear
104
+
77
105
  var url = (0, _utils.normalizeUrl)(linkUrl).trim();
78
106
  var markType = schema.mark('link', {
79
107
  href: url
@@ -11,6 +11,7 @@ var _card = require("@atlaskit/editor-common/card");
11
11
  var _keymaps = require("@atlaskit/editor-common/keymaps");
12
12
  var _utils = require("@atlaskit/editor-common/utils");
13
13
  var _keymap = require("@atlaskit/editor-prosemirror/keymap");
14
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
14
15
  var _commands = require("../commands");
15
16
  var _main = require("../pm-plugins/main");
16
17
  var _toolbarButtons = require("./toolbar-buttons");
@@ -32,6 +33,10 @@ function createKeymapPlugin(editorAnalyticsApi) {
32
33
  }, list);
33
34
  return (0, _keymap.keymap)(list);
34
35
  }
36
+
37
+ /**
38
+ * Convert the last word before the selection to a hyperlink if it's a valid URL with a tld we want to linkify
39
+ */
35
40
  var mayConvertLastWordToHyperlink = function mayConvertLastWordToHyperlink(editorAnalyticsApi) {
36
41
  return function (state, dispatch) {
37
42
  var _toolbarKey$getState$, _toolbarKey$getState;
@@ -44,6 +49,11 @@ var mayConvertLastWordToHyperlink = function mayConvertLastWordToHyperlink(edito
44
49
  var lastWord = words[words.length - 1];
45
50
  var match = (0, _adfSchema.getLinkMatch)(lastWord);
46
51
  if (match) {
52
+ if ((0, _platformFeatureFlags.getBooleanFF)('platform.linking-platform.prevent-suspicious-linkification')) {
53
+ if (!(0, _utils.shouldAutoLinkifyMatch)(match)) {
54
+ return false;
55
+ }
56
+ }
47
57
  var hyperlinkedText = match.raw;
48
58
  var start = state.selection.$from.pos - hyperlinkedText.length;
49
59
  var end = state.selection.$from.pos;
@@ -1,11 +1,22 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
- exports.toolbarKey = exports.toolbarButtonsPlugin = exports.prependToolbarButtons = void 0;
7
+ exports.toolbarKey = exports.toolbarButtonsPlugin = exports.prependToolbarButtons = exports.addToolbarItems = void 0;
8
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
7
11
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
8
12
  var _state = require("@atlaskit/editor-prosemirror/state");
13
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
14
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
15
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
16
+ /**
17
+ * Relative placement of an item
18
+ */
19
+
9
20
  var toolbarKey = exports.toolbarKey = new _state.PluginKey('hyperlinkToolbarItems');
10
21
  var prependToolbarButtons = exports.prependToolbarButtons = function prependToolbarButtons(_ref) {
11
22
  var items = _ref.items,
@@ -23,14 +34,52 @@ var prependToolbarButtons = exports.prependToolbarButtons = function prependTool
23
34
  });
24
35
  dispatch(tr);
25
36
  };
37
+ var addToolbarItems = exports.addToolbarItems = function addToolbarItems(_ref2) {
38
+ var items = _ref2.items,
39
+ placement = _ref2.placement,
40
+ view = _ref2.view;
41
+ var tr = view.state.tr,
42
+ dispatch = view.dispatch;
43
+ tr.setMeta(toolbarKey, {
44
+ items: items,
45
+ placement: placement
46
+ });
47
+ dispatch(tr);
48
+ };
49
+ var VALID_PLACEMENTS = ['start', 'end'];
50
+ var isValidPlacement = function isValidPlacement(placement) {
51
+ return VALID_PLACEMENTS.some(function (p) {
52
+ return p === placement;
53
+ });
54
+ };
26
55
  var toolbarButtonsPlugin = exports.toolbarButtonsPlugin = function toolbarButtonsPlugin() {
27
56
  return new _safePlugin.SafePlugin({
28
57
  key: toolbarKey,
29
58
  state: {
30
- init: function init(_, state) {
59
+ init: function init(_, __) {
31
60
  return undefined;
32
61
  },
33
- apply: function apply(tr, pluginState) {
62
+ apply: (0, _platformFeatureFlags.getBooleanFF)('platform.editor.card.inject-settings-button') ? function (tr, pluginState) {
63
+ var metaState = tr.getMeta(toolbarKey);
64
+ if (metaState) {
65
+ if ((0, _typeof2.default)(metaState) === 'object' && 'placement' in metaState) {
66
+ var _pluginState$items_ne;
67
+ var placement = metaState.placement;
68
+ if (!isValidPlacement(placement)) {
69
+ return pluginState;
70
+ }
71
+ var previous = (_pluginState$items_ne = pluginState === null || pluginState === void 0 ? void 0 : pluginState.items_next) !== null && _pluginState$items_ne !== void 0 ? _pluginState$items_ne : {
72
+ start: [],
73
+ end: []
74
+ };
75
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
76
+ items_next: _objectSpread(_objectSpread({}, previous), {}, (0, _defineProperty2.default)({}, placement, [].concat((0, _toConsumableArray2.default)(previous[placement]), [metaState.items])))
77
+ });
78
+ }
79
+ return metaState;
80
+ }
81
+ return pluginState;
82
+ } : function (tr, pluginState) {
34
83
  var metaState = tr.getMeta(toolbarKey);
35
84
  if (metaState) {
36
85
  return metaState;
@@ -10,6 +10,7 @@ import { normalizeUrl } from '@atlaskit/editor-common/utils';
10
10
  import CogIcon from '@atlaskit/icon/glyph/editor/settings';
11
11
  import UnlinkIcon from '@atlaskit/icon/glyph/editor/unlink';
12
12
  import OpenIcon from '@atlaskit/icon/glyph/shortcut';
13
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
13
14
  import { editInsertedLink, insertLinkWithAnalytics, onClickAwayCallback, onEscapeCallback, removeLink, updateLink } from './commands';
14
15
  import { stateKey } from './pm-plugins/main';
15
16
  import { toolbarKey } from './pm-plugins/toolbar-buttons';
@@ -91,6 +92,16 @@ const getSettingsButtonGroup = (intl, editorAnalyticsApi) => [{
91
92
  href: getLinkPreferencesURLFromENV(),
92
93
  target: '_blank'
93
94
  }];
95
+ export const mergeAddedItems = (link, ...handlerParams) => (items, toolbarItemsState) => {
96
+ const positions = toolbarItemsState === null || toolbarItemsState === void 0 ? void 0 : toolbarItemsState.items_next;
97
+ if (!positions) {
98
+ return items;
99
+ }
100
+ const start = positions.start;
101
+ const end = positions.end;
102
+ const reduceItems = (items = []) => items.reduce((acc, fn) => [...acc, ...fn(...handlerParams, link)], []);
103
+ return [...reduceItems(start), ...items, ...reduceItems(end)];
104
+ };
94
105
  export const getToolbarConfig = (options, pluginInjectionApi) => (state, intl, providerFactory) => {
95
106
  var _pluginInjectionApi$a, _options$lpLinkPicker;
96
107
  if (options.disableFloatingToolbar) {
@@ -135,55 +146,57 @@ export const getToolbarConfig = (options, pluginInjectionApi) => (state, intl, p
135
146
  if (activeLinkMark.node.text) {
136
147
  metadata.title = activeLinkMark.node.text;
137
148
  }
149
+ const baseItems = [...((_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$ : []), {
150
+ id: 'editor.link.edit',
151
+ testId: 'editor.link.edit',
152
+ type: 'button',
153
+ onClick: editInsertedLink(editorAnalyticsApi),
154
+ title: editLink,
155
+ showTitle: true,
156
+ metadata: metadata
157
+ }, {
158
+ type: 'separator'
159
+ }, {
160
+ id: 'editor.link.openLink',
161
+ testId: 'editor.link.openLink',
162
+ type: 'button',
163
+ disabled: !isValidUrl,
164
+ target: '_blank',
165
+ href: isValidUrl ? link : undefined,
166
+ onClick: visitHyperlink(editorAnalyticsApi),
167
+ title: labelOpenLink,
168
+ icon: OpenIcon,
169
+ className: 'hyperlink-open-link',
170
+ metadata: metadata,
171
+ tabIndex: null
172
+ }, {
173
+ type: 'separator'
174
+ }, {
175
+ id: 'editor.link.unlink',
176
+ testId: 'editor.link.unlink',
177
+ type: 'button',
178
+ onClick: commandWithMetadata(removeLink(pos, editorAnalyticsApi), {
179
+ inputMethod: INPUT_METHOD.FLOATING_TB
180
+ }),
181
+ title: labelUnlink,
182
+ icon: UnlinkIcon,
183
+ tabIndex: null
184
+ }, {
185
+ type: 'copy-button',
186
+ items: [{
187
+ type: 'separator'
188
+ }, {
189
+ state,
190
+ formatMessage: formatMessage,
191
+ markType: state.schema.marks.link
192
+ }]
193
+ }, ...(getBooleanFF('platform.editor.card.inject-settings-button') ? [] : getSettingsButtonGroup(intl, editorAnalyticsApi))];
194
+ const items = getBooleanFF('platform.editor.card.inject-settings-button') ? mergeAddedItems(link, state, intl, providerFactory)(baseItems, toolbarKey.getState(state)) : baseItems;
138
195
  return {
139
196
  ...hyperLinkToolbar,
140
197
  height: 32,
141
198
  width: 250,
142
- 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$ : []), {
143
- id: 'editor.link.edit',
144
- testId: 'editor.link.edit',
145
- type: 'button',
146
- onClick: editInsertedLink(editorAnalyticsApi),
147
- title: editLink,
148
- showTitle: true,
149
- metadata: metadata
150
- }, {
151
- type: 'separator'
152
- }, {
153
- id: 'editor.link.openLink',
154
- testId: 'editor.link.openLink',
155
- type: 'button',
156
- disabled: !isValidUrl,
157
- target: '_blank',
158
- href: isValidUrl ? link : undefined,
159
- onClick: visitHyperlink(editorAnalyticsApi),
160
- title: labelOpenLink,
161
- icon: OpenIcon,
162
- className: 'hyperlink-open-link',
163
- metadata: metadata,
164
- tabIndex: null
165
- }, {
166
- type: 'separator'
167
- }, {
168
- id: 'editor.link.unlink',
169
- testId: 'editor.link.unlink',
170
- type: 'button',
171
- onClick: commandWithMetadata(removeLink(pos, editorAnalyticsApi), {
172
- inputMethod: INPUT_METHOD.FLOATING_TB
173
- }),
174
- title: labelUnlink,
175
- icon: UnlinkIcon,
176
- tabIndex: null
177
- }, {
178
- type: 'copy-button',
179
- items: [{
180
- type: 'separator'
181
- }, {
182
- state,
183
- formatMessage: formatMessage,
184
- markType: state.schema.marks.link
185
- }]
186
- }, ...getSettingsButtonGroup(intl, editorAnalyticsApi)],
199
+ items,
187
200
  scrollable: true
188
201
  };
189
202
  }
@@ -10,7 +10,7 @@ import fakeCursorToolbarPlugin from './pm-plugins/fake-cursor-for-toolbar';
10
10
  import { createInputRulePlugin } from './pm-plugins/input-rule';
11
11
  import { createKeymapPlugin } from './pm-plugins/keymap';
12
12
  import { plugin, stateKey } from './pm-plugins/main';
13
- import { prependToolbarButtons, toolbarButtonsPlugin } from './pm-plugins/toolbar-buttons';
13
+ import { addToolbarItems, prependToolbarButtons, toolbarButtonsPlugin } from './pm-plugins/toolbar-buttons';
14
14
  import { getToolbarConfig } from './Toolbar';
15
15
  /**
16
16
  * Hyperlink plugin to be added to an `EditorPresetBuilder` and used with `ComposableEditor`
@@ -36,6 +36,7 @@ export const hyperlinkPlugin = ({
36
36
  },
37
37
  actions: {
38
38
  prependToolbarButtons,
39
+ addToolbarItems,
39
40
  hideLinkToolbar: hideLinkToolbarSetMeta,
40
41
  insertLink: (inputMethod, from, to, href, title, displayText, cardsAvailable = false, sourceEvent = undefined, appearance) => {
41
42
  var _api$analytics2;
@@ -1,9 +1,14 @@
1
1
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
2
  import { addLinkMetadata } from '@atlaskit/editor-common/card';
3
3
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
4
- import { createRule, findFilepaths, getLinkCreationAnalyticsEvent, isLinkInMatches, LinkMatcher, normalizeUrl } from '@atlaskit/editor-common/utils';
4
+ import { createRule, findFilepaths, getLinkCreationAnalyticsEvent, isLinkInMatches, LinkMatcher, normalizeUrl, shouldAutoLinkifyMatch } from '@atlaskit/editor-common/utils';
5
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
5
6
  import { createPlugin } from '@atlaskit/prosemirror-input-rules';
6
7
  import { toolbarKey } from './toolbar-buttons';
8
+
9
+ /**
10
+ * Called when space after link, but not on enter
11
+ */
7
12
  export function createLinkInputRule(regexp, editorAnalyticsApi) {
8
13
  // Plain typed text (eg, typing 'www.google.com') should convert to a hyperlink
9
14
  return createRule(regexp, (state, match, start, end) => {
@@ -15,7 +20,27 @@ export function createLinkInputRule(regexp, editorAnalyticsApi) {
15
20
  return null;
16
21
  }
17
22
  const link = match;
18
- const url = normalizeUrl(link.url);
23
+ let url;
24
+ if (getBooleanFF('platform.linking-platform.prevent-suspicious-linkification')) {
25
+ // Property 'url' does not exist on type 'RegExpExecArray', the type of `match`.
26
+ // This check is in case the match is not a Linkify match, which has a url property.
27
+ if (link.url === undefined) {
28
+ return null;
29
+ }
30
+ if (!shouldAutoLinkifyMatch(link)) {
31
+ return null;
32
+ }
33
+ url = normalizeUrl(link.url);
34
+
35
+ // Not previously handled; don't create a link if the URL is empty.
36
+ // This will only happen if the `regexp` matches more links than the normalizeUrl validation;
37
+ // if they both use the same linkify instance this shouldn't happen.
38
+ if (url === '') {
39
+ return null;
40
+ }
41
+ } else {
42
+ url = normalizeUrl(link.url);
43
+ }
19
44
  const markType = schema.mark('link', {
20
45
  href: url
21
46
  });
@@ -65,6 +90,10 @@ export function createInputRulePlugin(schema, editorAnalyticsApi) {
65
90
  schema
66
91
  } = state;
67
92
  const [, prefix, linkText, linkUrl] = match;
93
+
94
+ // We don't filter this match here by shouldAutoLinkifyMatch
95
+ // because the intent of creating a link is clear
96
+
68
97
  const url = normalizeUrl(linkUrl).trim();
69
98
  const markType = schema.mark('link', {
70
99
  href: url
@@ -2,8 +2,9 @@ import { getLinkMatch } from '@atlaskit/adf-schema';
2
2
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
3
3
  import { addLinkMetadata } from '@atlaskit/editor-common/card';
4
4
  import { addLink, bindKeymapWithCommand, bindKeymapWithEditorCommand, enter, escape, insertNewLine } from '@atlaskit/editor-common/keymaps';
5
- import { findFilepaths, getLinkCreationAnalyticsEvent, isLinkInMatches } from '@atlaskit/editor-common/utils';
5
+ import { findFilepaths, getLinkCreationAnalyticsEvent, isLinkInMatches, shouldAutoLinkifyMatch } from '@atlaskit/editor-common/utils';
6
6
  import { keymap } from '@atlaskit/editor-prosemirror/keymap';
7
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
7
8
  import { hideLinkToolbar, showLinkToolbar } from '../commands';
8
9
  import { stateKey } from '../pm-plugins/main';
9
10
  import { toolbarKey } from './toolbar-buttons';
@@ -25,6 +26,10 @@ export function createKeymapPlugin(editorAnalyticsApi) {
25
26
  }, list);
26
27
  return keymap(list);
27
28
  }
29
+
30
+ /**
31
+ * Convert the last word before the selection to a hyperlink if it's a valid URL with a tld we want to linkify
32
+ */
28
33
  const mayConvertLastWordToHyperlink = editorAnalyticsApi => {
29
34
  return function (state, dispatch) {
30
35
  var _toolbarKey$getState$, _toolbarKey$getState;
@@ -37,6 +42,11 @@ const mayConvertLastWordToHyperlink = editorAnalyticsApi => {
37
42
  const lastWord = words[words.length - 1];
38
43
  const match = getLinkMatch(lastWord);
39
44
  if (match) {
45
+ if (getBooleanFF('platform.linking-platform.prevent-suspicious-linkification')) {
46
+ if (!shouldAutoLinkifyMatch(match)) {
47
+ return false;
48
+ }
49
+ }
40
50
  const hyperlinkedText = match.raw;
41
51
  const start = state.selection.$from.pos - hyperlinkedText.length;
42
52
  const end = state.selection.$from.pos;
@@ -1,5 +1,11 @@
1
1
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
2
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
3
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
4
+
5
+ /**
6
+ * Relative placement of an item
7
+ */
8
+
3
9
  export const toolbarKey = new PluginKey('hyperlinkToolbarItems');
4
10
  export const prependToolbarButtons = ({
5
11
  items,
@@ -22,14 +28,59 @@ export const prependToolbarButtons = ({
22
28
  });
23
29
  dispatch(tr);
24
30
  };
31
+ export const addToolbarItems = ({
32
+ items,
33
+ placement,
34
+ view
35
+ }) => {
36
+ const {
37
+ state: {
38
+ tr
39
+ },
40
+ dispatch
41
+ } = view;
42
+ tr.setMeta(toolbarKey, {
43
+ items,
44
+ placement
45
+ });
46
+ dispatch(tr);
47
+ };
48
+ const VALID_PLACEMENTS = ['start', 'end'];
49
+ const isValidPlacement = placement => {
50
+ return VALID_PLACEMENTS.some(p => p === placement);
51
+ };
25
52
  export const toolbarButtonsPlugin = () => {
26
53
  return new SafePlugin({
27
54
  key: toolbarKey,
28
55
  state: {
29
- init: (_, state) => {
56
+ init: (_, __) => {
30
57
  return undefined;
31
58
  },
32
- apply: (tr, pluginState) => {
59
+ apply: getBooleanFF('platform.editor.card.inject-settings-button') ? (tr, pluginState) => {
60
+ const metaState = tr.getMeta(toolbarKey);
61
+ if (metaState) {
62
+ if (typeof metaState === 'object' && 'placement' in metaState) {
63
+ var _pluginState$items_ne;
64
+ const placement = metaState.placement;
65
+ if (!isValidPlacement(placement)) {
66
+ return pluginState;
67
+ }
68
+ const previous = (_pluginState$items_ne = pluginState === null || pluginState === void 0 ? void 0 : pluginState.items_next) !== null && _pluginState$items_ne !== void 0 ? _pluginState$items_ne : {
69
+ start: [],
70
+ end: []
71
+ };
72
+ return {
73
+ ...pluginState,
74
+ items_next: {
75
+ ...previous,
76
+ [placement]: [...previous[placement], metaState.items]
77
+ }
78
+ };
79
+ }
80
+ return metaState;
81
+ }
82
+ return pluginState;
83
+ } : (tr, pluginState) => {
33
84
  const metaState = tr.getMeta(toolbarKey);
34
85
  if (metaState) {
35
86
  return metaState;
@@ -1,5 +1,5 @@
1
- import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
3
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
4
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
5
  import React from 'react';
@@ -14,6 +14,7 @@ import { normalizeUrl } from '@atlaskit/editor-common/utils';
14
14
  import CogIcon from '@atlaskit/icon/glyph/editor/settings';
15
15
  import UnlinkIcon from '@atlaskit/icon/glyph/editor/unlink';
16
16
  import OpenIcon from '@atlaskit/icon/glyph/shortcut';
17
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
17
18
  import { editInsertedLink, insertLinkWithAnalytics, onClickAwayCallback, onEscapeCallback, removeLink, updateLink } from './commands';
18
19
  import { stateKey } from './pm-plugins/main';
19
20
  import { toolbarKey } from './pm-plugins/toolbar-buttons';
@@ -100,6 +101,26 @@ var getSettingsButtonGroup = function getSettingsButtonGroup(intl, editorAnalyti
100
101
  target: '_blank'
101
102
  }];
102
103
  };
104
+ export var mergeAddedItems = function mergeAddedItems(link) {
105
+ for (var _len = arguments.length, handlerParams = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
106
+ handlerParams[_key - 1] = arguments[_key];
107
+ }
108
+ return function (items, toolbarItemsState) {
109
+ var positions = toolbarItemsState === null || toolbarItemsState === void 0 ? void 0 : toolbarItemsState.items_next;
110
+ if (!positions) {
111
+ return items;
112
+ }
113
+ var start = positions.start;
114
+ var end = positions.end;
115
+ var reduceItems = function reduceItems() {
116
+ var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
117
+ return items.reduce(function (acc, fn) {
118
+ return [].concat(_toConsumableArray(acc), _toConsumableArray(fn.apply(void 0, handlerParams.concat([link]))));
119
+ }, []);
120
+ };
121
+ return [].concat(_toConsumableArray(reduceItems(start)), _toConsumableArray(items), _toConsumableArray(reduceItems(end)));
122
+ };
123
+ };
103
124
  export var getToolbarConfig = function getToolbarConfig(options, pluginInjectionApi) {
104
125
  return function (state, intl, providerFactory) {
105
126
  var _pluginInjectionApi$a, _options$lpLinkPicker;
@@ -143,54 +164,56 @@ export var getToolbarConfig = function getToolbarConfig(options, pluginInjection
143
164
  if (activeLinkMark.node.text) {
144
165
  metadata.title = activeLinkMark.node.text;
145
166
  }
167
+ var baseItems = [].concat(_toConsumableArray((_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$ : []), [{
168
+ id: 'editor.link.edit',
169
+ testId: 'editor.link.edit',
170
+ type: 'button',
171
+ onClick: editInsertedLink(editorAnalyticsApi),
172
+ title: editLink,
173
+ showTitle: true,
174
+ metadata: metadata
175
+ }, {
176
+ type: 'separator'
177
+ }, {
178
+ id: 'editor.link.openLink',
179
+ testId: 'editor.link.openLink',
180
+ type: 'button',
181
+ disabled: !isValidUrl,
182
+ target: '_blank',
183
+ href: isValidUrl ? link : undefined,
184
+ onClick: visitHyperlink(editorAnalyticsApi),
185
+ title: labelOpenLink,
186
+ icon: OpenIcon,
187
+ className: 'hyperlink-open-link',
188
+ metadata: metadata,
189
+ tabIndex: null
190
+ }, {
191
+ type: 'separator'
192
+ }, {
193
+ id: 'editor.link.unlink',
194
+ testId: 'editor.link.unlink',
195
+ type: 'button',
196
+ onClick: commandWithMetadata(removeLink(pos, editorAnalyticsApi), {
197
+ inputMethod: INPUT_METHOD.FLOATING_TB
198
+ }),
199
+ title: labelUnlink,
200
+ icon: UnlinkIcon,
201
+ tabIndex: null
202
+ }, {
203
+ type: 'copy-button',
204
+ items: [{
205
+ type: 'separator'
206
+ }, {
207
+ state: state,
208
+ formatMessage: formatMessage,
209
+ markType: state.schema.marks.link
210
+ }]
211
+ }], _toConsumableArray(getBooleanFF('platform.editor.card.inject-settings-button') ? [] : getSettingsButtonGroup(intl, editorAnalyticsApi)));
212
+ var items = getBooleanFF('platform.editor.card.inject-settings-button') ? mergeAddedItems(link, state, intl, providerFactory)(baseItems, toolbarKey.getState(state)) : baseItems;
146
213
  return _objectSpread(_objectSpread({}, hyperLinkToolbar), {}, {
147
214
  height: 32,
148
215
  width: 250,
149
- items: [].concat(_toConsumableArray((_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$ : []), [{
150
- id: 'editor.link.edit',
151
- testId: 'editor.link.edit',
152
- type: 'button',
153
- onClick: editInsertedLink(editorAnalyticsApi),
154
- title: editLink,
155
- showTitle: true,
156
- metadata: metadata
157
- }, {
158
- type: 'separator'
159
- }, {
160
- id: 'editor.link.openLink',
161
- testId: 'editor.link.openLink',
162
- type: 'button',
163
- disabled: !isValidUrl,
164
- target: '_blank',
165
- href: isValidUrl ? link : undefined,
166
- onClick: visitHyperlink(editorAnalyticsApi),
167
- title: labelOpenLink,
168
- icon: OpenIcon,
169
- className: 'hyperlink-open-link',
170
- metadata: metadata,
171
- tabIndex: null
172
- }, {
173
- type: 'separator'
174
- }, {
175
- id: 'editor.link.unlink',
176
- testId: 'editor.link.unlink',
177
- type: 'button',
178
- onClick: commandWithMetadata(removeLink(pos, editorAnalyticsApi), {
179
- inputMethod: INPUT_METHOD.FLOATING_TB
180
- }),
181
- title: labelUnlink,
182
- icon: UnlinkIcon,
183
- tabIndex: null
184
- }, {
185
- type: 'copy-button',
186
- items: [{
187
- type: 'separator'
188
- }, {
189
- state: state,
190
- formatMessage: formatMessage,
191
- markType: state.schema.marks.link
192
- }]
193
- }], _toConsumableArray(getSettingsButtonGroup(intl, editorAnalyticsApi))),
216
+ items: items,
194
217
  scrollable: true
195
218
  });
196
219
  }
@@ -10,7 +10,7 @@ import fakeCursorToolbarPlugin from './pm-plugins/fake-cursor-for-toolbar';
10
10
  import { createInputRulePlugin } from './pm-plugins/input-rule';
11
11
  import { createKeymapPlugin } from './pm-plugins/keymap';
12
12
  import { plugin as _plugin, stateKey } from './pm-plugins/main';
13
- import { prependToolbarButtons, toolbarButtonsPlugin } from './pm-plugins/toolbar-buttons';
13
+ import { addToolbarItems, prependToolbarButtons, toolbarButtonsPlugin } from './pm-plugins/toolbar-buttons';
14
14
  import { getToolbarConfig } from './Toolbar';
15
15
  /**
16
16
  * Hyperlink plugin to be added to an `EditorPresetBuilder` and used with `ComposableEditor`
@@ -37,6 +37,7 @@ export var hyperlinkPlugin = function hyperlinkPlugin(_ref) {
37
37
  },
38
38
  actions: {
39
39
  prependToolbarButtons: prependToolbarButtons,
40
+ addToolbarItems: addToolbarItems,
40
41
  hideLinkToolbar: hideLinkToolbarSetMeta,
41
42
  insertLink: function insertLink(inputMethod, from, to, href, title, displayText) {
42
43
  var _api$analytics2;
@@ -2,9 +2,14 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
3
3
  import { addLinkMetadata } from '@atlaskit/editor-common/card';
4
4
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
5
- import { createRule, findFilepaths, getLinkCreationAnalyticsEvent, isLinkInMatches, LinkMatcher, normalizeUrl } from '@atlaskit/editor-common/utils';
5
+ import { createRule, findFilepaths, getLinkCreationAnalyticsEvent, isLinkInMatches, LinkMatcher, normalizeUrl, shouldAutoLinkifyMatch } from '@atlaskit/editor-common/utils';
6
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
6
7
  import { createPlugin } from '@atlaskit/prosemirror-input-rules';
7
8
  import { toolbarKey } from './toolbar-buttons';
9
+
10
+ /**
11
+ * Called when space after link, but not on enter
12
+ */
8
13
  export function createLinkInputRule(regexp, editorAnalyticsApi) {
9
14
  // Plain typed text (eg, typing 'www.google.com') should convert to a hyperlink
10
15
  return createRule(regexp, function (state, match, start, end) {
@@ -14,7 +19,27 @@ export function createLinkInputRule(regexp, editorAnalyticsApi) {
14
19
  return null;
15
20
  }
16
21
  var link = match;
17
- var url = normalizeUrl(link.url);
22
+ var url;
23
+ if (getBooleanFF('platform.linking-platform.prevent-suspicious-linkification')) {
24
+ // Property 'url' does not exist on type 'RegExpExecArray', the type of `match`.
25
+ // This check is in case the match is not a Linkify match, which has a url property.
26
+ if (link.url === undefined) {
27
+ return null;
28
+ }
29
+ if (!shouldAutoLinkifyMatch(link)) {
30
+ return null;
31
+ }
32
+ url = normalizeUrl(link.url);
33
+
34
+ // Not previously handled; don't create a link if the URL is empty.
35
+ // This will only happen if the `regexp` matches more links than the normalizeUrl validation;
36
+ // if they both use the same linkify instance this shouldn't happen.
37
+ if (url === '') {
38
+ return null;
39
+ }
40
+ } else {
41
+ url = normalizeUrl(link.url);
42
+ }
18
43
  var markType = schema.mark('link', {
19
44
  href: url
20
45
  });
@@ -65,6 +90,10 @@ export function createInputRulePlugin(schema, editorAnalyticsApi) {
65
90
  prefix = _match[1],
66
91
  linkText = _match[2],
67
92
  linkUrl = _match[3];
93
+
94
+ // We don't filter this match here by shouldAutoLinkifyMatch
95
+ // because the intent of creating a link is clear
96
+
68
97
  var url = normalizeUrl(linkUrl).trim();
69
98
  var markType = schema.mark('link', {
70
99
  href: url
@@ -2,8 +2,9 @@ import { getLinkMatch } from '@atlaskit/adf-schema';
2
2
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
3
3
  import { addLinkMetadata } from '@atlaskit/editor-common/card';
4
4
  import { addLink, bindKeymapWithCommand, bindKeymapWithEditorCommand, enter, escape, insertNewLine } from '@atlaskit/editor-common/keymaps';
5
- import { findFilepaths, getLinkCreationAnalyticsEvent, isLinkInMatches } from '@atlaskit/editor-common/utils';
5
+ import { findFilepaths, getLinkCreationAnalyticsEvent, isLinkInMatches, shouldAutoLinkifyMatch } from '@atlaskit/editor-common/utils';
6
6
  import { keymap } from '@atlaskit/editor-prosemirror/keymap';
7
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
7
8
  import { hideLinkToolbar, showLinkToolbar } from '../commands';
8
9
  import { stateKey } from '../pm-plugins/main';
9
10
  import { toolbarKey } from './toolbar-buttons';
@@ -25,6 +26,10 @@ export function createKeymapPlugin(editorAnalyticsApi) {
25
26
  }, list);
26
27
  return keymap(list);
27
28
  }
29
+
30
+ /**
31
+ * Convert the last word before the selection to a hyperlink if it's a valid URL with a tld we want to linkify
32
+ */
28
33
  var mayConvertLastWordToHyperlink = function mayConvertLastWordToHyperlink(editorAnalyticsApi) {
29
34
  return function (state, dispatch) {
30
35
  var _toolbarKey$getState$, _toolbarKey$getState;
@@ -37,6 +42,11 @@ var mayConvertLastWordToHyperlink = function mayConvertLastWordToHyperlink(edito
37
42
  var lastWord = words[words.length - 1];
38
43
  var match = getLinkMatch(lastWord);
39
44
  if (match) {
45
+ if (getBooleanFF('platform.linking-platform.prevent-suspicious-linkification')) {
46
+ if (!shouldAutoLinkifyMatch(match)) {
47
+ return false;
48
+ }
49
+ }
40
50
  var hyperlinkedText = match.raw;
41
51
  var start = state.selection.$from.pos - hyperlinkedText.length;
42
52
  var end = state.selection.$from.pos;
@@ -1,5 +1,16 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
+ import _typeof from "@babel/runtime/helpers/typeof";
4
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
5
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
1
6
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
7
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
8
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
9
+
10
+ /**
11
+ * Relative placement of an item
12
+ */
13
+
3
14
  export var toolbarKey = new PluginKey('hyperlinkToolbarItems');
4
15
  export var prependToolbarButtons = function prependToolbarButtons(_ref) {
5
16
  var items = _ref.items,
@@ -17,14 +28,52 @@ export var prependToolbarButtons = function prependToolbarButtons(_ref) {
17
28
  });
18
29
  dispatch(tr);
19
30
  };
31
+ export var addToolbarItems = function addToolbarItems(_ref2) {
32
+ var items = _ref2.items,
33
+ placement = _ref2.placement,
34
+ view = _ref2.view;
35
+ var tr = view.state.tr,
36
+ dispatch = view.dispatch;
37
+ tr.setMeta(toolbarKey, {
38
+ items: items,
39
+ placement: placement
40
+ });
41
+ dispatch(tr);
42
+ };
43
+ var VALID_PLACEMENTS = ['start', 'end'];
44
+ var isValidPlacement = function isValidPlacement(placement) {
45
+ return VALID_PLACEMENTS.some(function (p) {
46
+ return p === placement;
47
+ });
48
+ };
20
49
  export var toolbarButtonsPlugin = function toolbarButtonsPlugin() {
21
50
  return new SafePlugin({
22
51
  key: toolbarKey,
23
52
  state: {
24
- init: function init(_, state) {
53
+ init: function init(_, __) {
25
54
  return undefined;
26
55
  },
27
- apply: function apply(tr, pluginState) {
56
+ apply: getBooleanFF('platform.editor.card.inject-settings-button') ? function (tr, pluginState) {
57
+ var metaState = tr.getMeta(toolbarKey);
58
+ if (metaState) {
59
+ if (_typeof(metaState) === 'object' && 'placement' in metaState) {
60
+ var _pluginState$items_ne;
61
+ var placement = metaState.placement;
62
+ if (!isValidPlacement(placement)) {
63
+ return pluginState;
64
+ }
65
+ var previous = (_pluginState$items_ne = pluginState === null || pluginState === void 0 ? void 0 : pluginState.items_next) !== null && _pluginState$items_ne !== void 0 ? _pluginState$items_ne : {
66
+ start: [],
67
+ end: []
68
+ };
69
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
70
+ items_next: _objectSpread(_objectSpread({}, previous), {}, _defineProperty({}, placement, [].concat(_toConsumableArray(previous[placement]), [metaState.items])))
71
+ });
72
+ }
73
+ return metaState;
74
+ }
75
+ return pluginState;
76
+ } : function (tr, pluginState) {
28
77
  var metaState = tr.getMeta(toolbarKey);
29
78
  if (metaState) {
30
79
  return metaState;
@@ -1,8 +1,12 @@
1
1
  /// <reference types="react" />
2
+ import type { IntlShape } from 'react-intl-next';
2
3
  import type { HyperlinkAddToolbarProps } from '@atlaskit/editor-common/link';
3
- import type { ExtractInjectionAPI, FloatingToolbarHandler, HyperlinkPluginOptions } from '@atlaskit/editor-common/types';
4
+ import type { Command, ExtractInjectionAPI, FloatingToolbarHandler, FloatingToolbarItem, HyperlinkPluginOptions } from '@atlaskit/editor-common/types';
5
+ import type { EditorState } from '@atlaskit/editor-prosemirror/state';
6
+ import { type HyperlinkToolbarItemsState } from './pm-plugins/toolbar-buttons';
4
7
  import type { hyperlinkPlugin } from './index';
5
8
  export declare function HyperlinkAddToolbarWithState({ linkPickerOptions, onSubmit, displayText, displayUrl, providerFactory, view, onCancel, invokeMethod, lpLinkPicker, onClose, onEscapeCallback, onClickAwayCallback, pluginInjectionApi, }: HyperlinkAddToolbarProps & {
6
9
  pluginInjectionApi: any;
7
10
  }): JSX.Element;
11
+ export declare const mergeAddedItems: (link: string, state: EditorState, intl: IntlShape, providerFactory: import("@atlaskit/editor-common/provider-factory").ProviderFactory) => (items: Array<FloatingToolbarItem<Command>>, toolbarItemsState: HyperlinkToolbarItemsState | undefined) => Array<FloatingToolbarItem<Command>>;
8
12
  export declare const getToolbarConfig: (options: HyperlinkPluginOptions, pluginInjectionApi: ExtractInjectionAPI<typeof hyperlinkPlugin> | undefined) => FloatingToolbarHandler;
@@ -1,4 +1,4 @@
1
1
  export { hyperlinkPlugin } from './plugin';
2
2
  export type { HyperlinkPlugin } from './plugin';
3
3
  export type { HideLinkToolbar, ShowLinkToolbar, InsertLink, UpdateLink, } from './commands';
4
- export type { PrependToolbarButtons } from './pm-plugins/toolbar-buttons';
4
+ export type { PrependToolbarButtons, AddToolbarItems, } from './pm-plugins/toolbar-buttons';
@@ -2,7 +2,7 @@ import type { HyperlinkState } from '@atlaskit/editor-common/link';
2
2
  import type { HyperlinkPluginOptions, NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
3
3
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
4
4
  import type { HideLinkToolbar, InsertLink, ShowLinkToolbar, UpdateLink } from './commands';
5
- import type { PrependToolbarButtons } from './pm-plugins/toolbar-buttons';
5
+ import type { AddToolbarItems, PrependToolbarButtons } from './pm-plugins/toolbar-buttons';
6
6
  export type HyperlinkPlugin = NextEditorPlugin<'hyperlink', {
7
7
  pluginConfiguration: HyperlinkPluginOptions | undefined;
8
8
  dependencies: [OptionalPlugin<AnalyticsPlugin>];
@@ -16,6 +16,10 @@ export type HyperlinkPlugin = NextEditorPlugin<'hyperlink', {
16
16
  * - onInsertLinkCallback (optional): To be called when a link is inserted and it can be changed into a card.
17
17
  */
18
18
  prependToolbarButtons: PrependToolbarButtons;
19
+ /**
20
+ * Add items before or after any default hyperlink floating toolbar items
21
+ */
22
+ addToolbarItems: AddToolbarItems;
19
23
  hideLinkToolbar: HideLinkToolbar;
20
24
  insertLink: InsertLink;
21
25
  updateLink: UpdateLink;
@@ -2,6 +2,9 @@ import type { EditorAnalyticsAPI } from '@atlaskit/editor-common/analytics';
2
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
3
  import type { InputRuleWrapper } from '@atlaskit/editor-common/types';
4
4
  import type { Schema } from '@atlaskit/editor-prosemirror/model';
5
+ /**
6
+ * Called when space after link, but not on enter
7
+ */
5
8
  export declare function createLinkInputRule(regexp: RegExp, editorAnalyticsApi: EditorAnalyticsAPI | undefined): InputRuleWrapper;
6
9
  export declare function createInputRulePlugin(schema: Schema, editorAnalyticsApi: EditorAnalyticsAPI | undefined): SafePlugin | undefined;
7
10
  export default createInputRulePlugin;
@@ -6,8 +6,14 @@ import type { FloatingToolbarItem } from '@atlaskit/editor-common/types';
6
6
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
7
7
  import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
8
8
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
9
- type HyperlinkToolbarItemsState = {
9
+ /**
10
+ * Relative placement of an item
11
+ */
12
+ type PlacementType = 'start' | 'end';
13
+ type AdditionalToolbarItems = Record<PlacementType, GetToolbarItems[]>;
14
+ export type HyperlinkToolbarItemsState = {
10
15
  items: GetToolbarItems;
16
+ items_next?: AdditionalToolbarItems;
11
17
  onEscapeCallback: ((tr: Transaction) => Transaction) | undefined;
12
18
  onInsertLinkCallback: QueueCardsFromTransactionAction | undefined;
13
19
  /**
@@ -24,5 +30,12 @@ interface PrependToolbarButtonsProps extends HyperlinkToolbarItemsState {
24
30
  }
25
31
  export type PrependToolbarButtons = (props: PrependToolbarButtonsProps) => void;
26
32
  export declare const prependToolbarButtons: ({ items, onEscapeCallback, onInsertLinkCallback, skipAnalytics, view, }: PrependToolbarButtonsProps) => void;
27
- export declare const toolbarButtonsPlugin: () => SafePlugin<undefined>;
33
+ type AddToolbarItemsProps = {
34
+ items: GetToolbarItems;
35
+ placement: PlacementType;
36
+ view: EditorView;
37
+ };
38
+ export type AddToolbarItems = (props: AddToolbarItemsProps) => void;
39
+ export declare const addToolbarItems: ({ items, placement, view, }: AddToolbarItemsProps) => void;
40
+ export declare const toolbarButtonsPlugin: () => SafePlugin<HyperlinkToolbarItemsState | undefined>;
28
41
  export {};
@@ -1,8 +1,12 @@
1
1
  /// <reference types="react" />
2
+ import type { IntlShape } from 'react-intl-next';
2
3
  import type { HyperlinkAddToolbarProps } from '@atlaskit/editor-common/link';
3
- import type { ExtractInjectionAPI, FloatingToolbarHandler, HyperlinkPluginOptions } from '@atlaskit/editor-common/types';
4
+ import type { Command, ExtractInjectionAPI, FloatingToolbarHandler, FloatingToolbarItem, HyperlinkPluginOptions } from '@atlaskit/editor-common/types';
5
+ import type { EditorState } from '@atlaskit/editor-prosemirror/state';
6
+ import { type HyperlinkToolbarItemsState } from './pm-plugins/toolbar-buttons';
4
7
  import type { hyperlinkPlugin } from './index';
5
8
  export declare function HyperlinkAddToolbarWithState({ linkPickerOptions, onSubmit, displayText, displayUrl, providerFactory, view, onCancel, invokeMethod, lpLinkPicker, onClose, onEscapeCallback, onClickAwayCallback, pluginInjectionApi, }: HyperlinkAddToolbarProps & {
6
9
  pluginInjectionApi: any;
7
10
  }): JSX.Element;
11
+ export declare const mergeAddedItems: (link: string, state: EditorState, intl: IntlShape, providerFactory: import("@atlaskit/editor-common/provider-factory").ProviderFactory) => (items: Array<FloatingToolbarItem<Command>>, toolbarItemsState: HyperlinkToolbarItemsState | undefined) => Array<FloatingToolbarItem<Command>>;
8
12
  export declare const getToolbarConfig: (options: HyperlinkPluginOptions, pluginInjectionApi: ExtractInjectionAPI<typeof hyperlinkPlugin> | undefined) => FloatingToolbarHandler;
@@ -1,4 +1,4 @@
1
1
  export { hyperlinkPlugin } from './plugin';
2
2
  export type { HyperlinkPlugin } from './plugin';
3
3
  export type { HideLinkToolbar, ShowLinkToolbar, InsertLink, UpdateLink, } from './commands';
4
- export type { PrependToolbarButtons } from './pm-plugins/toolbar-buttons';
4
+ export type { PrependToolbarButtons, AddToolbarItems, } from './pm-plugins/toolbar-buttons';
@@ -2,7 +2,7 @@ import type { HyperlinkState } from '@atlaskit/editor-common/link';
2
2
  import type { HyperlinkPluginOptions, NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/types';
3
3
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
4
4
  import type { HideLinkToolbar, InsertLink, ShowLinkToolbar, UpdateLink } from './commands';
5
- import type { PrependToolbarButtons } from './pm-plugins/toolbar-buttons';
5
+ import type { AddToolbarItems, PrependToolbarButtons } from './pm-plugins/toolbar-buttons';
6
6
  export type HyperlinkPlugin = NextEditorPlugin<'hyperlink', {
7
7
  pluginConfiguration: HyperlinkPluginOptions | undefined;
8
8
  dependencies: [
@@ -18,6 +18,10 @@ export type HyperlinkPlugin = NextEditorPlugin<'hyperlink', {
18
18
  * - onInsertLinkCallback (optional): To be called when a link is inserted and it can be changed into a card.
19
19
  */
20
20
  prependToolbarButtons: PrependToolbarButtons;
21
+ /**
22
+ * Add items before or after any default hyperlink floating toolbar items
23
+ */
24
+ addToolbarItems: AddToolbarItems;
21
25
  hideLinkToolbar: HideLinkToolbar;
22
26
  insertLink: InsertLink;
23
27
  updateLink: UpdateLink;
@@ -2,6 +2,9 @@ import type { EditorAnalyticsAPI } from '@atlaskit/editor-common/analytics';
2
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
3
  import type { InputRuleWrapper } from '@atlaskit/editor-common/types';
4
4
  import type { Schema } from '@atlaskit/editor-prosemirror/model';
5
+ /**
6
+ * Called when space after link, but not on enter
7
+ */
5
8
  export declare function createLinkInputRule(regexp: RegExp, editorAnalyticsApi: EditorAnalyticsAPI | undefined): InputRuleWrapper;
6
9
  export declare function createInputRulePlugin(schema: Schema, editorAnalyticsApi: EditorAnalyticsAPI | undefined): SafePlugin | undefined;
7
10
  export default createInputRulePlugin;
@@ -6,8 +6,14 @@ import type { FloatingToolbarItem } from '@atlaskit/editor-common/types';
6
6
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
7
7
  import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
8
8
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
9
- type HyperlinkToolbarItemsState = {
9
+ /**
10
+ * Relative placement of an item
11
+ */
12
+ type PlacementType = 'start' | 'end';
13
+ type AdditionalToolbarItems = Record<PlacementType, GetToolbarItems[]>;
14
+ export type HyperlinkToolbarItemsState = {
10
15
  items: GetToolbarItems;
16
+ items_next?: AdditionalToolbarItems;
11
17
  onEscapeCallback: ((tr: Transaction) => Transaction) | undefined;
12
18
  onInsertLinkCallback: QueueCardsFromTransactionAction | undefined;
13
19
  /**
@@ -24,5 +30,12 @@ interface PrependToolbarButtonsProps extends HyperlinkToolbarItemsState {
24
30
  }
25
31
  export type PrependToolbarButtons = (props: PrependToolbarButtonsProps) => void;
26
32
  export declare const prependToolbarButtons: ({ items, onEscapeCallback, onInsertLinkCallback, skipAnalytics, view, }: PrependToolbarButtonsProps) => void;
27
- export declare const toolbarButtonsPlugin: () => SafePlugin<undefined>;
33
+ type AddToolbarItemsProps = {
34
+ items: GetToolbarItems;
35
+ placement: PlacementType;
36
+ view: EditorView;
37
+ };
38
+ export type AddToolbarItems = (props: AddToolbarItemsProps) => void;
39
+ export declare const addToolbarItems: ({ items, placement, view, }: AddToolbarItemsProps) => void;
40
+ export declare const toolbarButtonsPlugin: () => SafePlugin<HyperlinkToolbarItemsState | undefined>;
28
41
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-hyperlink",
3
- "version": "1.3.1",
3
+ "version": "1.5.0",
4
4
  "description": "Hyperlink plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -34,10 +34,11 @@
34
34
  "dependencies": {
35
35
  "@atlaskit/adf-schema": "^35.8.0",
36
36
  "@atlaskit/analytics-next": "^9.2.0",
37
- "@atlaskit/editor-common": "^78.22.0",
37
+ "@atlaskit/editor-common": "^78.27.0",
38
38
  "@atlaskit/editor-plugin-analytics": "^1.0.0",
39
39
  "@atlaskit/editor-prosemirror": "3.0.0",
40
40
  "@atlaskit/icon": "^22.1.0",
41
+ "@atlaskit/platform-feature-flags": "^0.2.5",
41
42
  "@atlaskit/prosemirror-input-rules": "^3.0.0",
42
43
  "@babel/runtime": "^7.0.0",
43
44
  "uuid": "^3.1.0"
@@ -91,5 +92,12 @@
91
92
  }
92
93
  },
93
94
  "prettier": "@atlassian/atlassian-frontend-prettier-config-1.0.0",
94
- "platform-feature-flags": {}
95
+ "platform-feature-flags": {
96
+ "platform.linking-platform.prevent-suspicious-linkification": {
97
+ "type": "boolean"
98
+ },
99
+ "platform.editor.card.inject-settings-button": {
100
+ "type": "boolean"
101
+ }
102
+ }
95
103
  }