Skip to content

Commit 82e72fa

Browse files
feat(richtext-lexical, ui): add icon if link opens in new tab (#9211)
https://github.com/user-attachments/assets/46eebd2f-3965-40be-a7c6-e68446d32398 --------- Co-authored-by: Tylan Davis <hello@tylandavis.com>
1 parent 20c8992 commit 82e72fa

File tree

5 files changed

+37
-10
lines changed

5 files changed

+37
-10
lines changed

packages/richtext-lexical/src/features/link/client/plugins/floatingLinkEditor/LinkEditor/index.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { getTranslation } from '@payloadcms/translations'
88
import {
99
CloseMenuIcon,
1010
EditIcon,
11+
ExternalLinkIcon,
1112
formatDrawerSlug,
1213
useConfig,
1314
useEditDepth,
@@ -26,6 +27,7 @@ import {
2627
} from 'lexical'
2728
import React, { useCallback, useEffect, useRef, useState } from 'react'
2829

30+
import type { LinkNode } from '../../../../nodes/LinkNode.js'
2931
import type { LinkFields } from '../../../../nodes/types.js'
3032
import type { LinkPayload } from '../types.js'
3133

@@ -40,6 +42,9 @@ import { TOGGLE_LINK_WITH_MODAL_COMMAND } from './commands.js'
4042

4143
export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.ReactNode {
4244
const [editor] = useLexicalComposerContext()
45+
// TO-DO: There are several states that should not be state, because they
46+
// are derived from linkNode (linkUrl, linkLabel, stateData, isLink, isAutoLink...)
47+
const [linkNode, setLinkNode] = useState<LinkNode>()
4348

4449
const editorRef = useRef<HTMLDivElement | null>(null)
4550
const [linkUrl, setLinkUrl] = useState<null | string>(null)
@@ -116,6 +121,7 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
116121
setNotLink()
117122
return
118123
}
124+
setLinkNode(focusLinkParent)
119125

120126
const fields = focusLinkParent.getFields()
121127

@@ -328,10 +334,14 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
328334
<div className="link-input">
329335
{linkUrl && linkUrl.length > 0 ? (
330336
<a href={linkUrl} rel="noopener noreferrer" target="_blank">
337+
{linkNode?.__fields.newTab ? <ExternalLinkIcon /> : null}
331338
{linkLabel != null && linkLabel.length > 0 ? linkLabel : linkUrl}
332339
</a>
333340
) : linkLabel != null && linkLabel.length > 0 ? (
334-
<span className="link-input__label-pure">{linkLabel}</span>
341+
<>
342+
{linkNode?.__fields.newTab ? <ExternalLinkIcon /> : null}
343+
<span className="link-input__label-pure">{linkLabel}</span>
344+
</>
335345
) : null}
336346

337347
{editor.isEditable() && (

packages/richtext-lexical/src/features/link/client/plugins/floatingLinkEditor/index.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
position: relative;
3434
font-family: var(--font-body);
3535

36+
.icon--externalLink {
37+
margin-right: 5px;
38+
}
39+
3640
&__label-pure {
3741
color: var(--theme-elevation-1000);
3842
margin-right: 15px;

packages/ui/src/exports/client/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ export { Account } from '../../graphics/Account/index.js'
190190
export { PayloadIcon } from '../../graphics/Icon/index.js'
191191

192192
export { DefaultBlockImage } from '../../graphics/DefaultBlockImage/index.js'
193+
export { ExternalLinkIcon } from '../../graphics/ExternalLink/index.js'
193194
export { File } from '../../graphics/File/index.js'
194195

195196
// icons
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
@import '../../scss/styles';
2+
3+
@layer payload-default {
4+
.icon--externalLink {
5+
height: $baseline;
6+
width: $baseline;
7+
shape-rendering: auto;
8+
9+
.stroke {
10+
fill: none;
11+
stroke: currentColor;
12+
}
13+
}
14+
}

packages/ui/src/graphics/ExternalLink/index.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
import React from 'react'
22

3+
import './index.scss'
4+
35
export const ExternalLinkIcon: React.FC<{
46
className?: string
57
}> = (props) => {
68
const { className } = props
79
return (
810
<svg
9-
className={className}
10-
clipRule="evenodd"
11-
fillRule="evenodd"
12-
height="100%"
13-
viewBox="0 0 24 24"
14-
width="100%"
11+
className={[className, 'icon icon--externalLink'].filter(Boolean).join(' ')}
12+
viewBox="0 0 20 20"
1513
xmlns="http://www.w3.org/2000/svg"
1614
>
1715
<path
18-
d="M14 4h-13v18h20v-11h1v12h-22v-20h14v1zm10 5h-1v-6.293l-11.646 11.647-.708-.708 11.647-11.646h-6.293v-1h8v8z"
19-
fill="none"
20-
stroke="currentColor"
16+
className="stroke"
17+
d="M16 10.6667V14.6667C16 15.0203 15.8595 15.3594 15.6095 15.6095C15.3594 15.8595 15.0203 16 14.6667 16H5.33333C4.97971 16 4.64057 15.8595 4.39052 15.6095C4.14048 15.3594 4 15.0203 4 14.6667V5.33333C4 4.97971 4.14048 4.64057 4.39052 4.39052C4.64057 4.14048 4.97971 4 5.33333 4H9.33333M16 4L10 10M16 4H12M16 4V8"
18+
strokeLinecap="square"
2119
/>
2220
</svg>
2321
)

0 commit comments

Comments
 (0)