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:
@@ -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() && (
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
14
packages/ui/src/graphics/ExternalLink/index.scss
Normal file
14
packages/ui/src/graphics/ExternalLink/index.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user