mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: add link extension support and styling to editor components
This commit is contained in:
parent
1350cb7354
commit
e6d7e881e3
7 changed files with 297 additions and 5 deletions
118
apps/web/components/Objects/Editor/Toolbar/LinkInputTooltip.tsx
Normal file
118
apps/web/components/Objects/Editor/Toolbar/LinkInputTooltip.tsx
Normal 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
|
||||
|
|
@ -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={() =>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue