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>
This commit is contained in:
Germán Jabloñski
2024-11-15 11:55:43 -03:00
committed by GitHub
parent 20c899286e
commit 82e72fa7f2
5 changed files with 37 additions and 10 deletions

View File

@@ -8,6 +8,7 @@ import { getTranslation } from '@payloadcms/translations'
import {
CloseMenuIcon,
EditIcon,
ExternalLinkIcon,
formatDrawerSlug,
useConfig,
useEditDepth,
@@ -26,6 +27,7 @@ import {
} from 'lexical'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import type { LinkNode } from '../../../../nodes/LinkNode.js'
import type { LinkFields } from '../../../../nodes/types.js'
import type { LinkPayload } from '../types.js'
@@ -40,6 +42,9 @@ import { TOGGLE_LINK_WITH_MODAL_COMMAND } from './commands.js'
export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.ReactNode {
const [editor] = useLexicalComposerContext()
// TO-DO: There are several states that should not be state, because they
// are derived from linkNode (linkUrl, linkLabel, stateData, isLink, isAutoLink...)
const [linkNode, setLinkNode] = useState<LinkNode>()
const editorRef = useRef<HTMLDivElement | null>(null)
const [linkUrl, setLinkUrl] = useState<null | string>(null)
@@ -116,6 +121,7 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
setNotLink()
return
}
setLinkNode(focusLinkParent)
const fields = focusLinkParent.getFields()
@@ -328,10 +334,14 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
<div className="link-input">
{linkUrl && linkUrl.length > 0 ? (
<a href={linkUrl} rel="noopener noreferrer" target="_blank">
{linkNode?.__fields.newTab ? <ExternalLinkIcon /> : null}
{linkLabel != null && linkLabel.length > 0 ? linkLabel : linkUrl}
</a>
) : linkLabel != null && linkLabel.length > 0 ? (
<span className="link-input__label-pure">{linkLabel}</span>
<>
{linkNode?.__fields.newTab ? <ExternalLinkIcon /> : null}
<span className="link-input__label-pure">{linkLabel}</span>
</>
) : null}
{editor.isEditable() && (

View File

@@ -33,6 +33,10 @@
position: relative;
font-family: var(--font-body);
.icon--externalLink {
margin-right: 5px;
}
&__label-pure {
color: var(--theme-elevation-1000);
margin-right: 15px;

View File

@@ -190,6 +190,7 @@ export { Account } from '../../graphics/Account/index.js'
export { PayloadIcon } from '../../graphics/Icon/index.js'
export { DefaultBlockImage } from '../../graphics/DefaultBlockImage/index.js'
export { ExternalLinkIcon } from '../../graphics/ExternalLink/index.js'
export { File } from '../../graphics/File/index.js'
// icons

View File

@@ -0,0 +1,14 @@
@import '../../scss/styles';
@layer payload-default {
.icon--externalLink {
height: $baseline;
width: $baseline;
shape-rendering: auto;
.stroke {
fill: none;
stroke: currentColor;
}
}
}

View File

@@ -1,23 +1,21 @@
import React from 'react'
import './index.scss'
export const ExternalLinkIcon: React.FC<{
className?: string
}> = (props) => {
const { className } = props
return (
<svg
className={className}
clipRule="evenodd"
fillRule="evenodd"
height="100%"
viewBox="0 0 24 24"
width="100%"
className={[className, 'icon icon--externalLink'].filter(Boolean).join(' ')}
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 4h-13v18h20v-11h1v12h-22v-20h14v1zm10 5h-1v-6.293l-11.646 11.647-.708-.708 11.647-11.646h-6.293v-1h8v8z"
fill="none"
stroke="currentColor"
className="stroke"
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"
strokeLinecap="square"
/>
</svg>
)