0
# Caret System
1
2
Advanced caret positioning and navigation system for precise text manipulation and cursor movement. Lexical's caret system provides fine-grained control over cursor positioning that goes beyond standard DOM selection capabilities.
3
4
## Capabilities
5
6
### Core Caret Types
7
8
Base interfaces and types for representing different caret positions.
9
10
```typescript { .api }
11
/**
12
* Base interface for all caret types
13
*/
14
interface BaseCaret {
15
type: CaretType;
16
}
17
18
/**
19
* Type of caret positioning
20
*/
21
type CaretType = 'text-point' | 'sibling' | 'child';
22
23
/**
24
* Direction for caret movement
25
*/
26
type CaretDirection = 'next' | 'previous';
27
28
/**
29
* Union type for node-based carets
30
*/
31
type NodeCaret = SiblingCaret | ChildCaret;
32
33
/**
34
* Union type for point-based carets
35
*/
36
type PointCaret = TextPointCaret | NodeCaret;
37
38
/**
39
* Root mode for caret traversal
40
*/
41
type RootMode = 'root-boundary' | 'no-boundary';
42
```
43
44
### Text Point Carets
45
46
Carets that point to specific positions within text nodes.
47
48
```typescript { .api }
49
/**
50
* Caret pointing to position within text node
51
*/
52
interface TextPointCaret extends BaseCaret {
53
type: 'text-point';
54
node: TextNode;
55
offset: number;
56
}
57
58
/**
59
* Slice of text point caret with additional metadata
60
*/
61
interface TextPointCaretSlice extends TextPointCaret {
62
textContent: string;
63
isFirst: boolean;
64
isLast: boolean;
65
}
66
67
/**
68
* Tuple type for text point caret slices
69
*/
70
type TextPointCaretSliceTuple = [TextPointCaretSlice, TextPointCaretSlice];
71
72
/**
73
* Create text point caret
74
* @param node - Text node to point to
75
* @param offset - Character offset within node
76
* @returns Text point caret
77
*/
78
function $getTextPointCaret(node: TextNode, offset: number): TextPointCaret;
79
80
/**
81
* Create text point caret slice
82
* @param node - Text node
83
* @param startOffset - Start offset
84
* @param endOffset - End offset
85
* @returns Text point caret slice
86
*/
87
function $getTextPointCaretSlice(
88
node: TextNode,
89
startOffset: number,
90
endOffset: number
91
): TextPointCaretSlice;
92
93
/**
94
* Get text node offset for caret
95
* @param caret - Text point caret
96
* @returns Character offset
97
*/
98
function $getTextNodeOffset(caret: TextPointCaret): number;
99
100
/**
101
* Check if caret is text point caret
102
* @param caret - Caret to check
103
* @returns True if text point caret
104
*/
105
function $isTextPointCaret(caret: unknown): caret is TextPointCaret;
106
107
/**
108
* Check if caret is text point caret slice
109
* @param caret - Caret to check
110
* @returns True if text point caret slice
111
*/
112
function $isTextPointCaretSlice(caret: unknown): caret is TextPointCaretSlice;
113
```
114
115
### Sibling Carets
116
117
Carets that point to positions relative to sibling nodes.
118
119
```typescript { .api }
120
/**
121
* Caret pointing to position relative to sibling
122
*/
123
interface SiblingCaret extends BaseCaret {
124
type: 'sibling';
125
node: LexicalNode;
126
direction: CaretDirection;
127
}
128
129
/**
130
* Create sibling caret
131
* @param node - Reference node
132
* @param direction - Direction relative to node
133
* @returns Sibling caret
134
*/
135
function $getSiblingCaret(node: LexicalNode, direction: CaretDirection): SiblingCaret;
136
137
/**
138
* Check if caret is sibling caret
139
* @param caret - Caret to check
140
* @returns True if sibling caret
141
*/
142
function $isSiblingCaret(caret: unknown): caret is SiblingCaret;
143
144
/**
145
* Rewind sibling caret to previous position
146
* @param caret - Sibling caret to rewind
147
* @returns Rewound caret
148
*/
149
function $rewindSiblingCaret(caret: SiblingCaret): SiblingCaret;
150
```
151
152
### Child Carets
153
154
Carets that point to positions within element nodes' children.
155
156
```typescript { .api }
157
/**
158
* Caret pointing to child position within element
159
*/
160
interface ChildCaret extends BaseCaret {
161
type: 'child';
162
node: ElementNode;
163
offset: number;
164
}
165
166
/**
167
* Create child caret
168
* @param node - Parent element node
169
* @param offset - Child index offset
170
* @returns Child caret
171
*/
172
function $getChildCaret(node: ElementNode, offset: number): ChildCaret;
173
174
/**
175
* Get child caret or self if not element
176
* @param node - Node to get caret for
177
* @param offset - Offset within node
178
* @returns Child caret or appropriate caret type
179
*/
180
function $getChildCaretOrSelf(node: LexicalNode, offset: number): PointCaret;
181
182
/**
183
* Get child caret at specific index
184
* @param node - Parent element node
185
* @param index - Child index
186
* @returns Child caret
187
*/
188
function $getChildCaretAtIndex(node: ElementNode, index: number): ChildCaret;
189
190
/**
191
* Get adjacent child caret
192
* @param caret - Current child caret
193
* @param direction - Direction to move
194
* @returns Adjacent child caret or null
195
*/
196
function $getAdjacentChildCaret(
197
caret: ChildCaret,
198
direction: CaretDirection
199
): ChildCaret | null;
200
201
/**
202
* Check if caret is child caret
203
* @param caret - Caret to check
204
* @returns True if child caret
205
*/
206
function $isChildCaret(caret: unknown): caret is ChildCaret;
207
208
/**
209
* Check if caret is node-based caret
210
* @param caret - Caret to check
211
* @returns True if node caret
212
*/
213
function $isNodeCaret(caret: unknown): caret is NodeCaret;
214
```
215
216
### Caret Ranges
217
218
Ranges defined by two carets for selection and text manipulation.
219
220
```typescript { .api }
221
/**
222
* Range defined by two carets
223
*/
224
interface CaretRange {
225
start: PointCaret;
226
end: PointCaret;
227
}
228
229
/**
230
* Create caret range
231
* @param start - Start caret
232
* @param end - End caret
233
* @returns Caret range
234
*/
235
function $getCaretRange(start: PointCaret, end: PointCaret): CaretRange;
236
237
/**
238
* Create collapsed caret range
239
* @param caret - Single caret for collapsed range
240
* @returns Collapsed caret range
241
*/
242
function $getCollapsedCaretRange(caret: PointCaret): CaretRange;
243
244
/**
245
* Extend caret to create range
246
* @param caret - Starting caret
247
* @param direction - Direction to extend
248
* @param distance - Distance to extend
249
* @returns Caret range
250
*/
251
function $extendCaretToRange(
252
caret: PointCaret,
253
direction: CaretDirection,
254
distance: number
255
): CaretRange;
256
```
257
258
### Caret Movement and Navigation
259
260
Functions for moving carets and navigating through the document.
261
262
```typescript { .api }
263
/**
264
* Get caret in specified direction
265
* @param caret - Starting caret
266
* @param direction - Direction to move
267
* @param rootMode - Root boundary mode
268
* @returns New caret position or null
269
*/
270
function $getCaretInDirection(
271
caret: PointCaret,
272
direction: CaretDirection,
273
rootMode?: RootMode
274
): PointCaret | null;
275
276
/**
277
* Get caret range in direction
278
* @param range - Starting range
279
* @param direction - Direction to move
280
* @param distance - Distance to move
281
* @returns New caret range
282
*/
283
function $getCaretRangeInDirection(
284
range: CaretRange,
285
direction: CaretDirection,
286
distance: number
287
): CaretRange;
288
289
/**
290
* Get adjacent sibling or parent sibling caret
291
* @param caret - Starting caret
292
* @param direction - Direction to search
293
* @returns Adjacent caret or null
294
*/
295
function $getAdjacentSiblingOrParentSiblingCaret(
296
caret: PointCaret,
297
direction: CaretDirection
298
): PointCaret | null;
299
300
/**
301
* Normalize caret to deepest equivalent position
302
* @param caret - Caret to normalize
303
* @returns Normalized caret
304
*/
305
function $normalizeCaret(caret: PointCaret): PointCaret;
306
307
/**
308
* Check if text point caret is extendable
309
* @param caret - Text point caret to check
310
* @param direction - Direction to extend
311
* @returns True if extendable
312
*/
313
function $isExtendableTextPointCaret(
314
caret: TextPointCaret,
315
direction: CaretDirection
316
): boolean;
317
```
318
319
### Caret Comparison and Ordering
320
321
Functions for comparing and ordering carets.
322
323
```typescript { .api }
324
/**
325
* Compare point caret ordering
326
* @param a - First caret
327
* @param b - Second caret
328
* @returns Comparison result (-1, 0, 1)
329
*/
330
function $comparePointCaretNext(a: PointCaret, b: PointCaret): -1 | 0 | 1;
331
332
/**
333
* Function type for flipping direction
334
*/
335
type FlipDirection = (direction: CaretDirection) => CaretDirection;
336
337
/**
338
* Flip caret direction
339
* @param direction - Direction to flip
340
* @returns Opposite direction
341
*/
342
function flipDirection(direction: CaretDirection): CaretDirection;
343
```
344
345
### Common Ancestor Operations
346
347
Functions for finding common ancestors and analyzing relationships between carets.
348
349
```typescript { .api }
350
/**
351
* Result of common ancestor analysis
352
*/
353
type CommonAncestorResult =
354
| CommonAncestorResultSame
355
| CommonAncestorResultAncestor
356
| CommonAncestorResultDescendant
357
| CommonAncestorResultBranch;
358
359
/**
360
* Same node result
361
*/
362
interface CommonAncestorResultSame {
363
type: 'same';
364
node: LexicalNode;
365
}
366
367
/**
368
* Ancestor relationship result
369
*/
370
interface CommonAncestorResultAncestor {
371
type: 'ancestor';
372
ancestor: LexicalNode;
373
descendant: LexicalNode;
374
}
375
376
/**
377
* Descendant relationship result
378
*/
379
interface CommonAncestorResultDescendant {
380
type: 'descendant';
381
ancestor: LexicalNode;
382
descendant: LexicalNode;
383
}
384
385
/**
386
* Branch relationship result
387
*/
388
interface CommonAncestorResultBranch {
389
type: 'branch';
390
commonAncestor: LexicalNode;
391
branchA: LexicalNode;
392
branchB: LexicalNode;
393
}
394
395
/**
396
* Find common ancestor of two carets
397
* @param a - First caret
398
* @param b - Second caret
399
* @returns Common ancestor analysis result
400
*/
401
function $getCommonAncestor(a: PointCaret, b: PointCaret): CommonAncestorResult;
402
403
/**
404
* Get branch order in common ancestor result
405
* @param result - Common ancestor result
406
* @returns Branch ordering information
407
*/
408
function $getCommonAncestorResultBranchOrder(
409
result: CommonAncestorResultBranch
410
): -1 | 0 | 1;
411
```
412
413
### Selection Integration
414
415
Functions for converting between carets and editor selections.
416
417
```typescript { .api }
418
/**
419
* Create caret from selection point
420
* @param point - Selection point
421
* @returns Corresponding caret
422
*/
423
function $caretFromPoint(point: Point): PointCaret;
424
425
/**
426
* Create caret range from selection
427
* @param selection - Editor selection
428
* @returns Corresponding caret range
429
*/
430
function $caretRangeFromSelection(selection: BaseSelection): CaretRange | null;
431
432
/**
433
* Set selection point from caret
434
* @param point - Selection point to update
435
* @param caret - Caret to convert
436
*/
437
function $setPointFromCaret(point: Point, caret: PointCaret): void;
438
439
/**
440
* Set editor selection from caret range
441
* @param range - Caret range to convert
442
*/
443
function $setSelectionFromCaretRange(range: CaretRange): void;
444
445
/**
446
* Update range selection from caret range
447
* @param selection - Range selection to update
448
* @param range - Caret range source
449
*/
450
function $updateRangeSelectionFromCaretRange(
451
selection: RangeSelection,
452
range: CaretRange
453
): void;
454
```
455
456
### Text Manipulation with Carets
457
458
Functions for text operations using caret positioning.
459
460
```typescript { .api }
461
/**
462
* Remove text within caret range
463
* @param range - Caret range defining text to remove
464
*/
465
function $removeTextFromCaretRange(range: CaretRange): void;
466
467
/**
468
* Split at point caret next position
469
* @param caret - Text point caret to split at
470
* @param options - Split options
471
* @returns Split result
472
*/
473
function $splitAtPointCaretNext(
474
caret: TextPointCaret,
475
options?: SplitAtPointCaretNextOptions
476
): [TextNode, TextNode];
477
478
/**
479
* Options for splitting at point caret
480
*/
481
interface SplitAtPointCaretNextOptions {
482
/** Whether to preserve formatting */
483
preserveFormat?: boolean;
484
/** Whether to update selection */
485
updateSelection?: boolean;
486
}
487
```
488
489
### Stepwise Iterator
490
491
Advanced iteration system for traversing carets.
492
493
```typescript { .api }
494
/**
495
* Configuration for stepwise iterator
496
*/
497
interface StepwiseIteratorConfig {
498
/** Starting caret position */
499
start: PointCaret;
500
/** Ending caret position */
501
end?: PointCaret;
502
/** Direction of iteration */
503
direction: CaretDirection;
504
/** Root boundary mode */
505
rootMode?: RootMode;
506
}
507
508
/**
509
* Create stepwise iterator for caret traversal
510
* @param config - Iterator configuration
511
* @returns Iterator function
512
*/
513
function makeStepwiseIterator(
514
config: StepwiseIteratorConfig
515
): () => PointCaret | null;
516
```
517
518
**Usage Examples:**
519
520
```typescript
521
import {
522
$getTextPointCaret,
523
$getSiblingCaret,
524
$getCaretRange,
525
$setSelectionFromCaretRange,
526
$caretRangeFromSelection,
527
$getCaretInDirection
528
} from "lexical";
529
530
// Working with text point carets
531
editor.update(() => {
532
const textNode = $createTextNode('Hello, world!');
533
const caret = $getTextPointCaret(textNode, 5); // Position after "Hello"
534
535
// Move caret in direction
536
const nextCaret = $getCaretInDirection(caret, 'next');
537
538
// Create caret range
539
const endCaret = $getTextPointCaret(textNode, 11); // Position after "world"
540
const range = $getCaretRange(caret, endCaret);
541
542
// Convert to selection
543
$setSelectionFromCaretRange(range);
544
});
545
546
// Working with sibling carets
547
editor.update(() => {
548
const paragraph = $createParagraphNode();
549
const siblingCaret = $getSiblingCaret(paragraph, 'next');
550
551
// Create range and select
552
const range = $getCollapsedCaretRange(siblingCaret);
553
$setSelectionFromCaretRange(range);
554
});
555
556
// Converting from selection to carets
557
editor.update(() => {
558
const selection = $getSelection();
559
if (selection) {
560
const caretRange = $caretRangeFromSelection(selection);
561
if (caretRange) {
562
console.log('Selection as caret range:', caretRange);
563
564
// Manipulate with caret precision
565
$removeTextFromCaretRange(caretRange);
566
}
567
}
568
});
569
570
// Advanced caret iteration
571
const iteratorConfig = {
572
start: $getTextPointCaret(textNode, 0),
573
end: $getTextPointCaret(textNode, textNode.getTextContent().length),
574
direction: 'next' as CaretDirection
575
};
576
577
const iterator = makeStepwiseIterator(iteratorConfig);
578
let currentCaret = iterator();
579
580
while (currentCaret) {
581
console.log('Current caret position:', currentCaret);
582
currentCaret = iterator();
583
}
584
```
585
586
The caret system provides unprecedented precision in cursor positioning and text manipulation, enabling advanced text editing features that go beyond standard DOM selection capabilities. It's particularly useful for complex text editing scenarios, custom selection behaviors, and precise cursor movement operations.