feat(BlockEditor): auto-open link popover when clicking on existing link
- add `linkRangeAt` import to detect link span under caret - handle `mouseup` on container to detect collapsed click inside a link mark - set `autoOpenLink` flag via ref and expand selection to full link range - pass `autoOpenLink` to toolbar state and use `initialPopover='link'` prop - initialize toolbar popover state from `initialPopover` prop - pre-fill link url and new-tab from existing mark when `initialPopover` is set - add `linkRangeAt` helper in `types.js` to find enclosing link range at offset
This commit is contained in:
@@ -23,12 +23,13 @@ const SIMPLE_BUTTONS = [
|
||||
{ type: 'code', label: <CodeSimpleIcon width={15} height={15} />, title: 'Code (Ctrl+E)', className: '' },
|
||||
];
|
||||
|
||||
export default function InlineToolbar({ rect, activeMarks, onToggleMark, onSetMark, onClearMarks, onPinChange, usedColors }) {
|
||||
export default function InlineToolbar({ rect, activeMarks, onToggleMark, onSetMark, onClearMarks, onPinChange, usedColors, initialPopover }) {
|
||||
const ref = useRef(null);
|
||||
const [pos, setPos] = useState({ top: 0, left: 0, flipped: false });
|
||||
const [popover, setPopover] = useState(null); // 'color' | 'highlight' | 'link' | null
|
||||
const [linkUrl, setLinkUrl] = useState('');
|
||||
const [linkNewTab, setLinkNewTab] = useState(false);
|
||||
const [popover, setPopover] = useState(initialPopover ?? null);
|
||||
const existingLink = initialPopover === 'link' ? activeMarks?.find(m => m.type === 'link') : null;
|
||||
const [linkUrl, setLinkUrl] = useState(existingLink?.href ?? '');
|
||||
const [linkNewTab, setLinkNewTab] = useState(existingLink ? !!existingLink.newTab : false);
|
||||
|
||||
useEffect(() => {
|
||||
onPinChange?.(popover !== null);
|
||||
|
||||
@@ -205,6 +205,33 @@ export function marksAtOffset(nodes, offset) {
|
||||
return last.marks ? last.marks.map(m => ({ ...m })) : [];
|
||||
}
|
||||
|
||||
// Trouve le début et la fin continus du span qui porte un lien à `offset`.
|
||||
// Retourne { start, end, mark } ou null si aucun lien à cet offset.
|
||||
export function linkRangeAt(nodes, offset) {
|
||||
if (!Array.isArray(nodes) || nodes.length === 0) return null;
|
||||
const marks = marksAtOffset(nodes, offset);
|
||||
const linkMark = marks.find(m => m.type === 'link');
|
||||
if (!linkMark) return null;
|
||||
const key = markKey(linkMark);
|
||||
let pos = 0;
|
||||
let start = null;
|
||||
let end = null;
|
||||
for (const node of nodes) {
|
||||
const len = node.text?.length ?? 0;
|
||||
const nodeEnd = pos + len;
|
||||
const hasLink = (node.marks ?? []).some(m => markKey(m) === key);
|
||||
if (hasLink) {
|
||||
if (start === null) start = pos;
|
||||
end = nodeEnd;
|
||||
} else if (start !== null) {
|
||||
break;
|
||||
}
|
||||
pos = nodeEnd;
|
||||
}
|
||||
if (start === null) return null;
|
||||
return { start, end, mark: linkMark };
|
||||
}
|
||||
|
||||
// Marks communes à toute la plage [start, end[. Si la plage est vide,
|
||||
// retourne les marks à l'offset `start`. Utile pour afficher l'état actif
|
||||
// du toolbar.
|
||||
|
||||
Reference in New Issue
Block a user