feat: add link extension support and styling to editor components

This commit is contained in:
swve 2025-04-17 15:25:29 +02:00
parent 1350cb7354
commit e6d7e881e3
7 changed files with 297 additions and 5 deletions

View file

@ -0,0 +1,118 @@
import React, { useState, useEffect } from 'react'
import styled from 'styled-components'
import { CheckIcon, Cross2Icon } from '@radix-ui/react-icons'
interface LinkInputTooltipProps {
onSave: (url: string) => void
onCancel: () => void
currentUrl?: string
}
const LinkInputTooltip: React.FC<LinkInputTooltipProps> = ({ onSave, onCancel, currentUrl }) => {
const [url, setUrl] = useState(currentUrl || '')
useEffect(() => {
setUrl(currentUrl || '')
}, [currentUrl])
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
if (url) {
// Ensure the URL has a protocol
const formattedUrl = url.startsWith('http://') || url.startsWith('https://')
? url
: `https://${url}`
onSave(formattedUrl)
}
}
return (
<TooltipContainer>
<Form onSubmit={handleSubmit}>
<Input
type="text"
placeholder="Enter URL"
value={url}
onChange={(e) => setUrl(e.target.value)}
autoFocus
/>
<ButtonGroup>
<SaveButton type="submit" disabled={!url}>
<CheckIcon />
</SaveButton>
<CancelButton type="button" onClick={onCancel}>
<Cross2Icon />
</CancelButton>
</ButtonGroup>
</Form>
</TooltipContainer>
)
}
const TooltipContainer = styled.div`
position: absolute;
top: 100%;
left: 0;
background: white;
border: 1px solid rgba(217, 217, 217, 0.5);
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
z-index: 1000;
padding: 8px;
margin-top: 4px;
`
const Form = styled.form`
display: flex;
align-items: center;
gap: 4px;
`
const Input = styled.input`
padding: 4px 8px;
border: 1px solid rgba(217, 217, 217, 0.5);
border-radius: 4px;
font-size: 12px;
width: 200px;
&:focus {
outline: none;
border-color: rgba(217, 217, 217, 0.8);
}
`
const ButtonGroup = styled.div`
display: flex;
gap: 2px;
`
const Button = styled.button`
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
border: none;
border-radius: 4px;
cursor: pointer;
background: rgba(217, 217, 217, 0.24);
transition: background 0.2s;
&:hover {
background: rgba(217, 217, 217, 0.48);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`
const SaveButton = styled(Button)`
color: #4CAF50;
`
const CancelButton = styled(Button)`
color: #F44336;
`
export default LinkInputTooltip

View file

@ -23,6 +23,7 @@ import {
FileText,
ImagePlus,
Lightbulb,
Link2,
MousePointerClick,
Sigma,
Table,
@ -34,9 +35,12 @@ import {
import { SiYoutube } from '@icons-pack/react-simple-icons'
import ToolTip from '@components/Objects/StyledElements/Tooltip/Tooltip'
import React from 'react'
import LinkInputTooltip from './LinkInputTooltip'
export const ToolbarButtons = ({ editor, props }: any) => {
const [showTableMenu, setShowTableMenu] = React.useState(false)
const [showLinkInput, setShowLinkInput] = React.useState(false)
const linkButtonRef = React.useRef<HTMLDivElement>(null)
if (!editor) {
return null
@ -83,6 +87,47 @@ export const ToolbarButtons = ({ editor, props }: any) => {
}
]
const handleLinkClick = () => {
// Store the current selection
const { from, to } = editor.state.selection
if (editor.isActive('link')) {
const currentLink = editor.getAttributes('link')
setShowLinkInput(true)
} else {
setShowLinkInput(true)
}
// Restore the selection after a small delay to ensure the tooltip is rendered
setTimeout(() => {
editor.commands.setTextSelection({ from, to })
}, 0)
}
const getCurrentLinkUrl = () => {
if (editor.isActive('link')) {
return editor.getAttributes('link').href
}
return ''
}
const handleLinkSave = (url: string) => {
editor
.chain()
.focus()
.setLink({
href: url,
target: '_blank',
rel: 'noopener noreferrer'
})
.run()
setShowLinkInput(false)
}
const handleLinkCancel = () => {
setShowLinkInput(false)
}
return (
<ToolButtonsWrapper>
<ToolBtn onClick={() => editor.chain().focus().undo().run()}>
@ -185,6 +230,24 @@ export const ToolbarButtons = ({ editor, props }: any) => {
<AlertTriangle size={15} />
</ToolBtn>
</ToolTip>
<ToolTip content={'Link'}>
<div style={{ position: 'relative' }}>
<ToolBtn
ref={linkButtonRef}
onClick={handleLinkClick}
className={editor.isActive('link') ? 'is-active' : ''}
>
<Link2 size={15} />
</ToolBtn>
{showLinkInput && (
<LinkInputTooltip
onSave={handleLinkSave}
onCancel={handleLinkCancel}
currentUrl={getCurrentLinkUrl()}
/>
)}
</div>
</ToolTip>
<ToolTip content={'Image'}>
<ToolBtn
onClick={() =>