0
# Floating Link Interface
1
2
Complete floating UI system for inserting and editing links with keyboard shortcuts, positioning, and form management. Provides modal-style interfaces that appear contextually based on user interactions.
3
4
## Capabilities
5
6
### Floating Link Triggers
7
8
#### triggerFloatingLink
9
10
Main entry point for showing floating link interface based on current mode and state.
11
12
```typescript { .api }
13
/**
14
* Trigger floating link interface based on current mode
15
* @param editor - Slate editor instance
16
* @param options - Optional focus behavior
17
*/
18
function triggerFloatingLink(
19
editor: SlateEditor,
20
options?: { focused?: boolean }
21
): void;
22
```
23
24
**Usage Examples:**
25
26
```typescript
27
import { triggerFloatingLink } from "@udecode/plate-link/react";
28
29
// Trigger from keyboard shortcut
30
function handleKeyboardShortcut(editor: SlateEditor) {
31
triggerFloatingLink(editor, { focused: true });
32
}
33
34
// Trigger from toolbar button
35
function handleToolbarClick(editor: SlateEditor) {
36
triggerFloatingLink(editor);
37
}
38
```
39
40
#### triggerFloatingLinkInsert
41
42
Specifically triggers the floating interface in insert mode for creating new links.
43
44
```typescript { .api }
45
/**
46
* Trigger floating link interface in insert mode
47
* @param editor - Slate editor instance
48
* @param options - Optional focus behavior
49
* @returns Whether the trigger was successful
50
*/
51
function triggerFloatingLinkInsert(
52
editor: SlateEditor,
53
options?: { focused?: boolean }
54
): boolean | undefined;
55
```
56
57
**Usage Examples:**
58
59
```typescript
60
import { triggerFloatingLinkInsert } from "@udecode/plate-link/react";
61
62
// Trigger insert mode
63
function handleAddLink(editor: SlateEditor) {
64
const success = triggerFloatingLinkInsert(editor, { focused: true });
65
if (!success) {
66
console.log("Cannot insert link in current context");
67
}
68
}
69
70
// Check selection before triggering
71
function handleToolbarInsert(editor: SlateEditor) {
72
if (editor.selection) {
73
triggerFloatingLinkInsert(editor);
74
} else {
75
alert("Please select text to create a link");
76
}
77
}
78
```
79
80
#### triggerFloatingLinkEdit
81
82
Triggers the floating interface in edit mode for modifying existing links.
83
84
```typescript { .api }
85
/**
86
* Trigger floating link interface in edit mode for existing links
87
* @param editor - Slate editor instance
88
* @returns Whether edit mode was successfully triggered
89
*/
90
function triggerFloatingLinkEdit(
91
editor: SlateEditor
92
): boolean | undefined;
93
```
94
95
**Usage Examples:**
96
97
```typescript
98
import { triggerFloatingLinkEdit } from "@udecode/plate-link/react";
99
100
// Trigger edit mode
101
function handleEditLink(editor: SlateEditor) {
102
const success = triggerFloatingLinkEdit(editor);
103
if (!success) {
104
console.log("No link found at current selection");
105
}
106
}
107
108
// Context menu integration
109
function handleLinkContextMenu(editor: SlateEditor, event: MouseEvent) {
110
event.preventDefault();
111
triggerFloatingLinkEdit(editor);
112
}
113
```
114
115
### Floating Insert Interface
116
117
#### useFloatingLinkInsert
118
119
Complete hook system for managing floating link insertion UI with positioning and form handling.
120
121
```typescript { .api }
122
/**
123
* Get state for floating link insert interface
124
* @param options - Optional floating toolbar configuration
125
* @returns State object with floating UI data
126
*/
127
function useFloatingLinkInsertState(options?: LinkFloatingToolbarState): {
128
floating: ReturnType<typeof useVirtualFloating>;
129
focused: boolean;
130
isOpen: boolean;
131
readOnly: boolean;
132
triggerFloatingLinkHotkeys: string;
133
};
134
135
/**
136
* Get props and behavior for floating link insert interface
137
* @param state - State from useFloatingLinkInsertState
138
* @returns Interface props, refs, and input handling
139
*/
140
function useFloatingLinkInsert(
141
state: ReturnType<typeof useFloatingLinkInsertState>
142
): {
143
hidden: boolean;
144
props: object;
145
ref: RefObject<HTMLElement>;
146
textInputProps: object;
147
};
148
149
interface LinkFloatingToolbarState {
150
floatingOptions?: UseVirtualFloatingOptions;
151
}
152
```
153
154
**Usage Examples:**
155
156
```typescript
157
import React from 'react';
158
import {
159
useFloatingLinkInsertState,
160
useFloatingLinkInsert,
161
FloatingLinkUrlInput
162
} from "@udecode/plate-link/react";
163
164
function FloatingLinkInsertPopover() {
165
const state = useFloatingLinkInsertState({
166
floatingOptions: {
167
placement: 'bottom-start',
168
offset: 4
169
}
170
});
171
172
const { hidden, props, ref, textInputProps } = useFloatingLinkInsert(state);
173
174
if (hidden) return null;
175
176
return (
177
<div
178
{...props}
179
ref={ref}
180
className="floating-link-popover"
181
>
182
<div className="popover-content">
183
<FloatingLinkUrlInput />
184
<div className="popover-actions">
185
<button type="submit">Add Link</button>
186
<button type="button" onClick={() => /* close */}>
187
Cancel
188
</button>
189
</div>
190
</div>
191
</div>
192
);
193
}
194
```
195
196
### Floating Edit Interface
197
198
#### useFloatingLinkEdit
199
200
Complete hook system for managing floating link editing UI with existing link data population.
201
202
```typescript { .api }
203
/**
204
* Get state for floating link edit interface
205
* @param options - Optional floating toolbar configuration
206
* @returns Comprehensive state with editor and floating data
207
*/
208
function useFloatingLinkEditState(options?: LinkFloatingToolbarState): {
209
editor: SlateEditor;
210
floating: ReturnType<typeof useVirtualFloating>;
211
isEditing: boolean;
212
isOpen: boolean;
213
readOnly: boolean;
214
triggerFloatingLinkHotkeys: string;
215
versionEditor: number;
216
};
217
218
/**
219
* Get props and behavior for floating link edit interface
220
* @param state - State from useFloatingLinkEditState
221
* @returns Edit interface props and button handlers
222
*/
223
function useFloatingLinkEdit(
224
state: ReturnType<typeof useFloatingLinkEditState>
225
): {
226
editButtonProps: object;
227
props: object;
228
ref: RefObject<HTMLElement>;
229
unlinkButtonProps: object;
230
};
231
```
232
233
**Usage Examples:**
234
235
```typescript
236
import React from 'react';
237
import {
238
useFloatingLinkEditState,
239
useFloatingLinkEdit,
240
FloatingLinkUrlInput,
241
FloatingLinkNewTabInput
242
} from "@udecode/plate-link/react";
243
244
function FloatingLinkEditPopover() {
245
const state = useFloatingLinkEditState();
246
const {
247
editButtonProps,
248
props,
249
ref,
250
unlinkButtonProps
251
} = useFloatingLinkEdit(state);
252
253
if (!state.isOpen) return null;
254
255
return (
256
<div
257
{...props}
258
ref={ref}
259
className="floating-link-edit-popover"
260
>
261
<div className="popover-content">
262
<FloatingLinkUrlInput />
263
<FloatingLinkNewTabInput />
264
265
<div className="popover-actions">
266
<button {...editButtonProps}>
267
Save Changes
268
</button>
269
<button {...unlinkButtonProps} className="btn-danger">
270
Remove Link
271
</button>
272
</div>
273
</div>
274
</div>
275
);
276
}
277
```
278
279
### Virtual Floating Positioning
280
281
#### useVirtualFloatingLink
282
283
Manages virtual floating positioning for link UI elements relative to editor selection.
284
285
```typescript { .api }
286
/**
287
* Manage virtual floating positioning for link UI
288
* @param config - Editor ID and floating options
289
* @returns Virtual floating positioning data
290
*/
291
function useVirtualFloatingLink({
292
editorId,
293
...floatingOptions
294
}: {
295
editorId: string;
296
} & UseVirtualFloatingOptions): ReturnType<typeof useVirtualFloating>;
297
```
298
299
**Usage Examples:**
300
301
```typescript
302
import React from 'react';
303
import { useVirtualFloatingLink } from "@udecode/plate-link/react";
304
import { useEditorPlugin } from '@udecode/plate/react';
305
import { LinkPlugin } from '@udecode/plate-link/react';
306
307
function CustomFloatingLink() {
308
const { editor } = useEditorPlugin(LinkPlugin);
309
310
const floating = useVirtualFloatingLink({
311
editorId: editor.id,
312
placement: 'bottom',
313
offset: 8,
314
autoUpdate: true
315
});
316
317
return (
318
<div
319
ref={floating.refs.setFloating}
320
style={{
321
position: floating.strategy,
322
top: floating.y ?? 0,
323
left: floating.x ?? 0,
324
zIndex: 1000
325
}}
326
className="custom-floating-link"
327
>
328
{/* Floating UI content */}
329
</div>
330
);
331
}
332
```
333
334
### Keyboard Handling
335
336
#### useFloatingLinkEscape
337
338
Handles escape key behavior for closing floating link interfaces.
339
340
```typescript { .api }
341
/**
342
* Handle escape key behavior for floating link UI
343
*/
344
function useFloatingLinkEscape(): void;
345
```
346
347
#### useFloatingLinkEnter
348
349
Handles enter key submission for floating link forms.
350
351
```typescript { .api }
352
/**
353
* Handle enter key submission for floating link forms
354
*/
355
function useFloatingLinkEnter(): void;
356
```
357
358
**Usage Examples:**
359
360
```typescript
361
import React from 'react';
362
import {
363
useFloatingLinkEscape,
364
useFloatingLinkEnter
365
} from "@udecode/plate-link/react";
366
367
function FloatingLinkForm() {
368
// Enable keyboard handling
369
useFloatingLinkEscape();
370
useFloatingLinkEnter();
371
372
return (
373
<form className="floating-link-form">
374
{/* Form fields */}
375
<input placeholder="Enter URL..." />
376
<div className="form-hint">
377
Press Enter to save, Escape to cancel
378
</div>
379
</form>
380
);
381
}
382
```
383
384
### Advanced Floating Patterns
385
386
#### Custom Floating UI with Full Control
387
388
```typescript
389
import React from 'react';
390
import {
391
useFloatingLinkInsertState,
392
useFloatingLinkInsert,
393
useFloatingLinkEscape,
394
useFloatingLinkEnter,
395
submitFloatingLink
396
} from "@udecode/plate-link/react";
397
import { useEditorPlugin } from '@udecode/plate/react';
398
import { LinkPlugin } from '@udecode/plate-link/react';
399
400
function AdvancedFloatingLink() {
401
const { editor } = useEditorPlugin(LinkPlugin);
402
403
const state = useFloatingLinkInsertState({
404
floatingOptions: {
405
placement: 'bottom-start',
406
middleware: [
407
{ name: 'offset', options: { mainAxis: 8 } },
408
{ name: 'flip' },
409
{ name: 'shift', options: { padding: 8 } }
410
]
411
}
412
});
413
414
const { hidden, props, ref } = useFloatingLinkInsert(state);
415
416
// Enable keyboard shortcuts
417
useFloatingLinkEscape();
418
useFloatingLinkEnter();
419
420
const handleSubmit = () => {
421
const success = submitFloatingLink(editor);
422
if (success) {
423
editor.api.floatingLink.hide();
424
}
425
};
426
427
if (hidden) return null;
428
429
return (
430
<div
431
{...props}
432
ref={ref}
433
className="advanced-floating-link"
434
>
435
<div className="floating-header">
436
<h3>Add Link</h3>
437
<button
438
onClick={() => editor.api.floatingLink.hide()}
439
className="close-button"
440
>
441
×
442
</button>
443
</div>
444
445
<form onSubmit={handleSubmit}>
446
<div className="form-fields">
447
<input placeholder="https://example.com" />
448
<label>
449
<input type="checkbox" />
450
Open in new tab
451
</label>
452
</div>
453
454
<div className="form-actions">
455
<button type="submit">Add Link</button>
456
<button type="button" onClick={() => editor.api.floatingLink.hide()}>
457
Cancel
458
</button>
459
</div>
460
</form>
461
</div>
462
);
463
}
464
```
465
466
#### Contextual Floating Behavior
467
468
```typescript
469
import {
470
triggerFloatingLinkInsert,
471
triggerFloatingLinkEdit
472
} from "@udecode/plate-link/react";
473
474
function handleFloatingLinkTrigger(editor: SlateEditor) {
475
// Check if cursor is in existing link
476
const linkEntry = editor.api.above({
477
match: { type: 'a' }
478
});
479
480
if (linkEntry) {
481
// Edit existing link
482
triggerFloatingLinkEdit(editor);
483
} else {
484
// Insert new link
485
triggerFloatingLinkInsert(editor, { focused: true });
486
}
487
}
488
489
// Keyboard shortcut handler
490
function setupFloatingLinkShortcut(editor: SlateEditor) {
491
const handleKeyDown = (event: KeyboardEvent) => {
492
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
493
event.preventDefault();
494
handleFloatingLinkTrigger(editor);
495
}
496
};
497
498
document.addEventListener('keydown', handleKeyDown);
499
return () => document.removeEventListener('keydown', handleKeyDown);
500
}
501
```