@atlaskit/editor-plugin-hyperlink 18.1.12 → 19.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.
- package/CHANGELOG.md +15 -0
- package/dist/cjs/editor-commands/commands.js +3 -1
- package/dist/cjs/pm-plugins/main.js +76 -0
- package/dist/es2019/editor-commands/commands.js +1 -1
- package/dist/es2019/pm-plugins/main.js +76 -0
- package/dist/esm/editor-commands/commands.js +3 -1
- package/dist/esm/pm-plugins/main.js +76 -0
- package/package.json +16 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-hyperlink
|
|
2
2
|
|
|
3
|
+
## 19.0.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`fb891566dfac2`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/fb891566dfac2) -
|
|
8
|
+
Add anyMarksActiveFromTr and getHadMarkAttributes so mark-active checks can run from a
|
|
9
|
+
transaction. Color, highlight, toolbar/Cmd+K link apply, and paste events include had-mark
|
|
10
|
+
attributes behind kill switch platform_editor_add_text_color_tracking.
|
|
11
|
+
- [`a075f90fa2e35`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a075f90fa2e35) -
|
|
12
|
+
Behind `platform_editor_middle_click_no_link_toolbar`: middle-clicking a link no longer opens the
|
|
13
|
+
link toolbar, and links in editable text now open in a new tab (browsers do not follow link clicks
|
|
14
|
+
inside `contenteditable`, so previously they did not open at all). Applies to plain hyperlinks and
|
|
15
|
+
to inline, block, embed and datasource smart links. Behaviour is unchanged when the gate is off.
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
|
|
3
18
|
## 18.1.12
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
|
@@ -173,7 +173,9 @@ var insertLinkWithAnalytics = exports.insertLinkWithAnalytics = function insertL
|
|
|
173
173
|
if (cardsAvailable && !title && !displayText) {
|
|
174
174
|
return insertLink(from, to, href, title, displayText, inputMethod, sourceEvent, appearance, cardActions);
|
|
175
175
|
}
|
|
176
|
-
return (0, _editorAnalytics.withAnalytics)(editorAnalyticsApi,
|
|
176
|
+
return (0, _editorAnalytics.withAnalytics)(editorAnalyticsApi, function (state) {
|
|
177
|
+
return (0, _utils.getLinkCreationAnalyticsEvent)(inputMethod, href, from !== to ? state.tr : undefined);
|
|
178
|
+
})(insertLink(from, to, href, title, displayText, inputMethod, sourceEvent, appearance, cardActions));
|
|
177
179
|
};
|
|
178
180
|
function removeLink(pos, editorAnalyticsApi) {
|
|
179
181
|
return (0, _card.commandWithMetadata)(setLinkHref('', pos, editorAnalyticsApi), {
|
|
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
});
|
|
7
7
|
exports.stateKey = exports.plugin = void 0;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _isSafeUrl = require("@atlaskit/adf-schema/is-safe-url");
|
|
10
|
+
var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
9
11
|
var _uuid = require("uuid");
|
|
10
12
|
var _link3 = require("@atlaskit/editor-common/link");
|
|
11
13
|
var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
|
|
@@ -197,12 +199,45 @@ var plugin = exports.plugin = function plugin(dispatch, intl, editorAppearance,
|
|
|
197
199
|
key: stateKey,
|
|
198
200
|
props: {
|
|
199
201
|
handleDOMEvents: {
|
|
202
|
+
// Ignored via go/ees005
|
|
203
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
204
|
+
auxclick: function auxclick(_, event) {
|
|
205
|
+
if (event.button !== 1) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
var anchor = getLinkAnchor(event);
|
|
209
|
+
if (!anchor) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Browsers do not follow a link clicked inside contenteditable, so a link in
|
|
214
|
+
// editable text never opens on middle-click and has to be opened here. Cards
|
|
215
|
+
// render their anchor inside a contenteditable="false" node view, and a
|
|
216
|
+
// read-only editor is itself contenteditable="false" — in both cases the
|
|
217
|
+
// browser opens the tab, so doing it again here would open two.
|
|
218
|
+
if (anchor.closest('[contenteditable="false"]')) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
var href = anchor.getAttribute('href');
|
|
222
|
+
if (!href || !(0, _isSafeUrl.isSafeUrl)(href)) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
if (!(0, _fg.fg)('platform_editor_middle_click_no_link_toolbar')) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
window.open(href, '_blank', 'noopener,noreferrer');
|
|
229
|
+
return true;
|
|
230
|
+
},
|
|
200
231
|
// Ignored via go/ees005
|
|
201
232
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
202
233
|
mouseup: function mouseup(_, event) {
|
|
203
234
|
// this prevents redundant selection transaction when clicking on link
|
|
204
235
|
// link state will be update on slection change which happens on mousedown
|
|
205
236
|
if (isLinkDirectTarget(event)) {
|
|
237
|
+
// Leave middle-click alone so the browser can still open the link in a new tab.
|
|
238
|
+
if (event.button === 1 && (0, _fg.fg)('platform_editor_middle_click_no_link_toolbar')) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
206
241
|
event.preventDefault();
|
|
207
242
|
return true;
|
|
208
243
|
}
|
|
@@ -211,6 +246,25 @@ var plugin = exports.plugin = function plugin(dispatch, intl, editorAppearance,
|
|
|
211
246
|
// Ignored via go/ees005
|
|
212
247
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
213
248
|
mousedown: function mousedown(view, event) {
|
|
249
|
+
// Middle-click on a link: skip ProseMirror's own handling so it does not move
|
|
250
|
+
// the selection into the link or select the card node, which is what opens
|
|
251
|
+
// the toolbar.
|
|
252
|
+
var middleClickAnchor = event.button === 1 ? getLinkAnchor(event) : null;
|
|
253
|
+
if (middleClickAnchor && (0, _fg.fg)('platform_editor_middle_click_no_link_toolbar')) {
|
|
254
|
+
// Returning true only stops ProseMirror. The browser still places a caret
|
|
255
|
+
// at the click point inside contenteditable, and a caret inside the link is
|
|
256
|
+
// itself enough to open the toolbar, so the default has to be suppressed
|
|
257
|
+
// too. The auxclick handler above opens the tab for these links.
|
|
258
|
+
//
|
|
259
|
+
// Anchors inside a contenteditable="false" node view (cards, and read-only
|
|
260
|
+
// editors) are deliberately left alone: they get no caret, and it is the
|
|
261
|
+
// browser's default that opens their new tab.
|
|
262
|
+
if (!middleClickAnchor.closest('[contenteditable="false"]')) {
|
|
263
|
+
event.preventDefault();
|
|
264
|
+
}
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
214
268
|
// since link clicks are disallowed by browsers inside contenteditable
|
|
215
269
|
// so we need to handle shift+click selection ourselves in this case
|
|
216
270
|
if (!event.shiftKey || !isLinkDirectTarget(event)) {
|
|
@@ -235,4 +289,26 @@ var plugin = exports.plugin = function plugin(dispatch, intl, editorAppearance,
|
|
|
235
289
|
};
|
|
236
290
|
function isLinkDirectTarget(event) {
|
|
237
291
|
return (event === null || event === void 0 ? void 0 : event.target) instanceof HTMLElement && event.target.tagName === 'A';
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Whether the event happened anywhere inside a link, rather than on the anchor itself.
|
|
296
|
+
*
|
|
297
|
+
* Matching on the anchor alone is not enough. Every smart link variant renders its anchor
|
|
298
|
+
* with nested content inside it — inline cards nest icon, title and lozenge spans, block
|
|
299
|
+
* and embed cards render anchors via the flexible card `base-link-element` and
|
|
300
|
+
* `ExpandedFrame` — so the direct target of a real click is almost never the anchor.
|
|
301
|
+
*
|
|
302
|
+
* Being DOM based rather than node based, this covers every variant that renders a real
|
|
303
|
+
* anchor: link marks, inline cards, block cards, embed card headers and datasource cell
|
|
304
|
+
* links. None of the card node views swallow `mousedown` (their `stopEvent` only returns
|
|
305
|
+
* true for `dragstart`, or for form elements in the datasource case), so the event does
|
|
306
|
+
* reach this handler in all of those cases. The exception is content inside an embed
|
|
307
|
+
* card's iframe, which is a separate document we cannot observe.
|
|
308
|
+
*
|
|
309
|
+
* `isLinkDirectTarget` is deliberately left alone: widening it would also change
|
|
310
|
+
* shift+click and mouseup handling for every nested element inside a link.
|
|
311
|
+
*/
|
|
312
|
+
function getLinkAnchor(event) {
|
|
313
|
+
return (event === null || event === void 0 ? void 0 : event.target) instanceof Element ? event.target.closest('a[href]') : null;
|
|
238
314
|
}
|
|
@@ -154,7 +154,7 @@ export const insertLinkWithAnalytics = (inputMethod, from, to, href, cardActions
|
|
|
154
154
|
if (cardsAvailable && !title && !displayText) {
|
|
155
155
|
return insertLink(from, to, href, title, displayText, inputMethod, sourceEvent, appearance, cardActions);
|
|
156
156
|
}
|
|
157
|
-
return withAnalytics(editorAnalyticsApi, getLinkCreationAnalyticsEvent(inputMethod, href))(insertLink(from, to, href, title, displayText, inputMethod, sourceEvent, appearance, cardActions));
|
|
157
|
+
return withAnalytics(editorAnalyticsApi, state => getLinkCreationAnalyticsEvent(inputMethod, href, from !== to ? state.tr : undefined))(insertLink(from, to, href, title, displayText, inputMethod, sourceEvent, appearance, cardActions));
|
|
158
158
|
};
|
|
159
159
|
export function removeLink(pos, editorAnalyticsApi) {
|
|
160
160
|
return commandWithMetadata(setLinkHref('', pos, editorAnalyticsApi), {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isSafeUrl } from '@atlaskit/adf-schema/is-safe-url';
|
|
2
|
+
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
1
3
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
2
4
|
import { v4 as uuid } from 'uuid';
|
|
3
5
|
import { InsertStatus, LinkAction, getActiveLinkMark } from '@atlaskit/editor-common/link';
|
|
@@ -196,12 +198,45 @@ export const plugin = (dispatch, intl, editorAppearance, _pluginInjectionApi, _o
|
|
|
196
198
|
key: stateKey,
|
|
197
199
|
props: {
|
|
198
200
|
handleDOMEvents: {
|
|
201
|
+
// Ignored via go/ees005
|
|
202
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
203
|
+
auxclick: (_, event) => {
|
|
204
|
+
if (event.button !== 1) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
const anchor = getLinkAnchor(event);
|
|
208
|
+
if (!anchor) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Browsers do not follow a link clicked inside contenteditable, so a link in
|
|
213
|
+
// editable text never opens on middle-click and has to be opened here. Cards
|
|
214
|
+
// render their anchor inside a contenteditable="false" node view, and a
|
|
215
|
+
// read-only editor is itself contenteditable="false" — in both cases the
|
|
216
|
+
// browser opens the tab, so doing it again here would open two.
|
|
217
|
+
if (anchor.closest('[contenteditable="false"]')) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
const href = anchor.getAttribute('href');
|
|
221
|
+
if (!href || !isSafeUrl(href)) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
if (!fg('platform_editor_middle_click_no_link_toolbar')) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
window.open(href, '_blank', 'noopener,noreferrer');
|
|
228
|
+
return true;
|
|
229
|
+
},
|
|
199
230
|
// Ignored via go/ees005
|
|
200
231
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
201
232
|
mouseup: (_, event) => {
|
|
202
233
|
// this prevents redundant selection transaction when clicking on link
|
|
203
234
|
// link state will be update on slection change which happens on mousedown
|
|
204
235
|
if (isLinkDirectTarget(event)) {
|
|
236
|
+
// Leave middle-click alone so the browser can still open the link in a new tab.
|
|
237
|
+
if (event.button === 1 && fg('platform_editor_middle_click_no_link_toolbar')) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
205
240
|
event.preventDefault();
|
|
206
241
|
return true;
|
|
207
242
|
}
|
|
@@ -210,6 +245,25 @@ export const plugin = (dispatch, intl, editorAppearance, _pluginInjectionApi, _o
|
|
|
210
245
|
// Ignored via go/ees005
|
|
211
246
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
212
247
|
mousedown: (view, event) => {
|
|
248
|
+
// Middle-click on a link: skip ProseMirror's own handling so it does not move
|
|
249
|
+
// the selection into the link or select the card node, which is what opens
|
|
250
|
+
// the toolbar.
|
|
251
|
+
const middleClickAnchor = event.button === 1 ? getLinkAnchor(event) : null;
|
|
252
|
+
if (middleClickAnchor && fg('platform_editor_middle_click_no_link_toolbar')) {
|
|
253
|
+
// Returning true only stops ProseMirror. The browser still places a caret
|
|
254
|
+
// at the click point inside contenteditable, and a caret inside the link is
|
|
255
|
+
// itself enough to open the toolbar, so the default has to be suppressed
|
|
256
|
+
// too. The auxclick handler above opens the tab for these links.
|
|
257
|
+
//
|
|
258
|
+
// Anchors inside a contenteditable="false" node view (cards, and read-only
|
|
259
|
+
// editors) are deliberately left alone: they get no caret, and it is the
|
|
260
|
+
// browser's default that opens their new tab.
|
|
261
|
+
if (!middleClickAnchor.closest('[contenteditable="false"]')) {
|
|
262
|
+
event.preventDefault();
|
|
263
|
+
}
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
|
|
213
267
|
// since link clicks are disallowed by browsers inside contenteditable
|
|
214
268
|
// so we need to handle shift+click selection ourselves in this case
|
|
215
269
|
if (!event.shiftKey || !isLinkDirectTarget(event)) {
|
|
@@ -239,4 +293,26 @@ export const plugin = (dispatch, intl, editorAppearance, _pluginInjectionApi, _o
|
|
|
239
293
|
});
|
|
240
294
|
function isLinkDirectTarget(event) {
|
|
241
295
|
return (event === null || event === void 0 ? void 0 : event.target) instanceof HTMLElement && event.target.tagName === 'A';
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Whether the event happened anywhere inside a link, rather than on the anchor itself.
|
|
300
|
+
*
|
|
301
|
+
* Matching on the anchor alone is not enough. Every smart link variant renders its anchor
|
|
302
|
+
* with nested content inside it — inline cards nest icon, title and lozenge spans, block
|
|
303
|
+
* and embed cards render anchors via the flexible card `base-link-element` and
|
|
304
|
+
* `ExpandedFrame` — so the direct target of a real click is almost never the anchor.
|
|
305
|
+
*
|
|
306
|
+
* Being DOM based rather than node based, this covers every variant that renders a real
|
|
307
|
+
* anchor: link marks, inline cards, block cards, embed card headers and datasource cell
|
|
308
|
+
* links. None of the card node views swallow `mousedown` (their `stopEvent` only returns
|
|
309
|
+
* true for `dragstart`, or for form elements in the datasource case), so the event does
|
|
310
|
+
* reach this handler in all of those cases. The exception is content inside an embed
|
|
311
|
+
* card's iframe, which is a separate document we cannot observe.
|
|
312
|
+
*
|
|
313
|
+
* `isLinkDirectTarget` is deliberately left alone: widening it would also change
|
|
314
|
+
* shift+click and mouseup handling for every nested element inside a link.
|
|
315
|
+
*/
|
|
316
|
+
function getLinkAnchor(event) {
|
|
317
|
+
return (event === null || event === void 0 ? void 0 : event.target) instanceof Element ? event.target.closest('a[href]') : null;
|
|
242
318
|
}
|
|
@@ -156,7 +156,9 @@ export var insertLinkWithAnalytics = function insertLinkWithAnalytics(inputMetho
|
|
|
156
156
|
if (cardsAvailable && !title && !displayText) {
|
|
157
157
|
return insertLink(from, to, href, title, displayText, inputMethod, sourceEvent, appearance, cardActions);
|
|
158
158
|
}
|
|
159
|
-
return withAnalytics(editorAnalyticsApi,
|
|
159
|
+
return withAnalytics(editorAnalyticsApi, function (state) {
|
|
160
|
+
return getLinkCreationAnalyticsEvent(inputMethod, href, from !== to ? state.tr : undefined);
|
|
161
|
+
})(insertLink(from, to, href, title, displayText, inputMethod, sourceEvent, appearance, cardActions));
|
|
160
162
|
};
|
|
161
163
|
export function removeLink(pos, editorAnalyticsApi) {
|
|
162
164
|
return commandWithMetadata(setLinkHref('', pos, editorAnalyticsApi), {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
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; }
|
|
3
3
|
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; }
|
|
4
|
+
import { isSafeUrl } from '@atlaskit/adf-schema/is-safe-url';
|
|
5
|
+
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
4
6
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
5
7
|
import { v4 as uuid } from 'uuid';
|
|
6
8
|
import { InsertStatus, LinkAction, getActiveLinkMark } from '@atlaskit/editor-common/link';
|
|
@@ -191,12 +193,45 @@ export var plugin = function plugin(dispatch, intl, editorAppearance, _pluginInj
|
|
|
191
193
|
key: stateKey,
|
|
192
194
|
props: {
|
|
193
195
|
handleDOMEvents: {
|
|
196
|
+
// Ignored via go/ees005
|
|
197
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
198
|
+
auxclick: function auxclick(_, event) {
|
|
199
|
+
if (event.button !== 1) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
var anchor = getLinkAnchor(event);
|
|
203
|
+
if (!anchor) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Browsers do not follow a link clicked inside contenteditable, so a link in
|
|
208
|
+
// editable text never opens on middle-click and has to be opened here. Cards
|
|
209
|
+
// render their anchor inside a contenteditable="false" node view, and a
|
|
210
|
+
// read-only editor is itself contenteditable="false" — in both cases the
|
|
211
|
+
// browser opens the tab, so doing it again here would open two.
|
|
212
|
+
if (anchor.closest('[contenteditable="false"]')) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
var href = anchor.getAttribute('href');
|
|
216
|
+
if (!href || !isSafeUrl(href)) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
if (!fg('platform_editor_middle_click_no_link_toolbar')) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
window.open(href, '_blank', 'noopener,noreferrer');
|
|
223
|
+
return true;
|
|
224
|
+
},
|
|
194
225
|
// Ignored via go/ees005
|
|
195
226
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
196
227
|
mouseup: function mouseup(_, event) {
|
|
197
228
|
// this prevents redundant selection transaction when clicking on link
|
|
198
229
|
// link state will be update on slection change which happens on mousedown
|
|
199
230
|
if (isLinkDirectTarget(event)) {
|
|
231
|
+
// Leave middle-click alone so the browser can still open the link in a new tab.
|
|
232
|
+
if (event.button === 1 && fg('platform_editor_middle_click_no_link_toolbar')) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
200
235
|
event.preventDefault();
|
|
201
236
|
return true;
|
|
202
237
|
}
|
|
@@ -205,6 +240,25 @@ export var plugin = function plugin(dispatch, intl, editorAppearance, _pluginInj
|
|
|
205
240
|
// Ignored via go/ees005
|
|
206
241
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
207
242
|
mousedown: function mousedown(view, event) {
|
|
243
|
+
// Middle-click on a link: skip ProseMirror's own handling so it does not move
|
|
244
|
+
// the selection into the link or select the card node, which is what opens
|
|
245
|
+
// the toolbar.
|
|
246
|
+
var middleClickAnchor = event.button === 1 ? getLinkAnchor(event) : null;
|
|
247
|
+
if (middleClickAnchor && fg('platform_editor_middle_click_no_link_toolbar')) {
|
|
248
|
+
// Returning true only stops ProseMirror. The browser still places a caret
|
|
249
|
+
// at the click point inside contenteditable, and a caret inside the link is
|
|
250
|
+
// itself enough to open the toolbar, so the default has to be suppressed
|
|
251
|
+
// too. The auxclick handler above opens the tab for these links.
|
|
252
|
+
//
|
|
253
|
+
// Anchors inside a contenteditable="false" node view (cards, and read-only
|
|
254
|
+
// editors) are deliberately left alone: they get no caret, and it is the
|
|
255
|
+
// browser's default that opens their new tab.
|
|
256
|
+
if (!middleClickAnchor.closest('[contenteditable="false"]')) {
|
|
257
|
+
event.preventDefault();
|
|
258
|
+
}
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
|
|
208
262
|
// since link clicks are disallowed by browsers inside contenteditable
|
|
209
263
|
// so we need to handle shift+click selection ourselves in this case
|
|
210
264
|
if (!event.shiftKey || !isLinkDirectTarget(event)) {
|
|
@@ -229,4 +283,26 @@ export var plugin = function plugin(dispatch, intl, editorAppearance, _pluginInj
|
|
|
229
283
|
};
|
|
230
284
|
function isLinkDirectTarget(event) {
|
|
231
285
|
return (event === null || event === void 0 ? void 0 : event.target) instanceof HTMLElement && event.target.tagName === 'A';
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Whether the event happened anywhere inside a link, rather than on the anchor itself.
|
|
290
|
+
*
|
|
291
|
+
* Matching on the anchor alone is not enough. Every smart link variant renders its anchor
|
|
292
|
+
* with nested content inside it — inline cards nest icon, title and lozenge spans, block
|
|
293
|
+
* and embed cards render anchors via the flexible card `base-link-element` and
|
|
294
|
+
* `ExpandedFrame` — so the direct target of a real click is almost never the anchor.
|
|
295
|
+
*
|
|
296
|
+
* Being DOM based rather than node based, this covers every variant that renders a real
|
|
297
|
+
* anchor: link marks, inline cards, block cards, embed card headers and datasource cell
|
|
298
|
+
* links. None of the card node views swallow `mousedown` (their `stopEvent` only returns
|
|
299
|
+
* true for `dragstart`, or for form elements in the datasource case), so the event does
|
|
300
|
+
* reach this handler in all of those cases. The exception is content inside an embed
|
|
301
|
+
* card's iframe, which is a separate document we cannot observe.
|
|
302
|
+
*
|
|
303
|
+
* `isLinkDirectTarget` is deliberately left alone: widening it would also change
|
|
304
|
+
* shift+click and mouseup handling for every nested element inside a link.
|
|
305
|
+
*/
|
|
306
|
+
function getLinkAnchor(event) {
|
|
307
|
+
return (event === null || event === void 0 ? void 0 : event.target) instanceof Element ? event.target.closest('a[href]') : null;
|
|
232
308
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-hyperlink",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "19.0.0",
|
|
4
4
|
"description": "Hyperlink plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -21,17 +21,17 @@
|
|
|
21
21
|
],
|
|
22
22
|
"atlaskit:src": "src/index.ts",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@atlaskit/adf-schema": "^57.
|
|
24
|
+
"@atlaskit/adf-schema": "^57.4.0",
|
|
25
25
|
"@atlaskit/analytics-next": "^12.5.0",
|
|
26
|
-
"@atlaskit/editor-plugin-analytics": "^
|
|
27
|
-
"@atlaskit/editor-plugin-card": "^
|
|
28
|
-
"@atlaskit/editor-plugin-connectivity": "^
|
|
29
|
-
"@atlaskit/editor-plugin-editor-viewmode": "^
|
|
30
|
-
"@atlaskit/editor-plugin-primary-toolbar": "^
|
|
31
|
-
"@atlaskit/editor-plugin-selection-toolbar": "^
|
|
32
|
-
"@atlaskit/editor-plugin-toolbar": "^
|
|
33
|
-
"@atlaskit/editor-plugin-ui-control-registry": "^
|
|
34
|
-
"@atlaskit/editor-plugin-user-intent": "^
|
|
26
|
+
"@atlaskit/editor-plugin-analytics": "^17.0.0",
|
|
27
|
+
"@atlaskit/editor-plugin-card": "^23.0.0",
|
|
28
|
+
"@atlaskit/editor-plugin-connectivity": "^17.0.0",
|
|
29
|
+
"@atlaskit/editor-plugin-editor-viewmode": "^19.0.0",
|
|
30
|
+
"@atlaskit/editor-plugin-primary-toolbar": "^18.0.0",
|
|
31
|
+
"@atlaskit/editor-plugin-selection-toolbar": "^18.0.0",
|
|
32
|
+
"@atlaskit/editor-plugin-toolbar": "^14.0.0",
|
|
33
|
+
"@atlaskit/editor-plugin-ui-control-registry": "^11.0.0",
|
|
34
|
+
"@atlaskit/editor-plugin-user-intent": "^15.0.0",
|
|
35
35
|
"@atlaskit/editor-prosemirror": "^8.0.0",
|
|
36
36
|
"@atlaskit/editor-toolbar": "^2.6.0",
|
|
37
37
|
"@atlaskit/editor-toolbar-model": "^1.2.0",
|
|
@@ -40,13 +40,13 @@
|
|
|
40
40
|
"@atlaskit/platform-feature-experiments": "^0.3.0",
|
|
41
41
|
"@atlaskit/platform-feature-flags": "^2.2.0",
|
|
42
42
|
"@atlaskit/prosemirror-input-rules": "^4.1.0",
|
|
43
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
43
|
+
"@atlaskit/tmp-editor-statsig": "^179.0.0",
|
|
44
44
|
"@babel/runtime": "^7.0.0",
|
|
45
45
|
"@emotion/react": "^11.7.1",
|
|
46
46
|
"uuid": "^11.1.1"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@atlaskit/editor-common": "^
|
|
49
|
+
"@atlaskit/editor-common": "^121.0.0",
|
|
50
50
|
"react": "^18.2.0 || ^19.2.0",
|
|
51
51
|
"react-dom": "^18.2.0 || ^19.2.0",
|
|
52
52
|
"react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
|
|
@@ -60,6 +60,9 @@
|
|
|
60
60
|
"wait-for-expect": "^1.2.0"
|
|
61
61
|
},
|
|
62
62
|
"platform-feature-flags": {
|
|
63
|
+
"platform_editor_middle_click_no_link_toolbar": {
|
|
64
|
+
"type": "boolean"
|
|
65
|
+
},
|
|
63
66
|
"platform_editor_use_preferences_plugin": {
|
|
64
67
|
"type": "boolean"
|
|
65
68
|
}
|