or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

conversion-utilities.mddata-models.mdeditor-components.mdentity-system.mdindex.mdkey-bindings-utilities.mdtext-modification.md
tile.json

text-modification.mddocs/

Text Modification

Utilities for making changes to editor content with proper immutable updates. These tools provide both low-level content manipulation and high-level rich text operations.

Capabilities

DraftModifier (exported as Modifier)

Low-level utility functions for modifying ContentState objects. All methods return new immutable ContentState instances.

/**
 * Low-level content modification utilities
 */
const Modifier: {
  /**
   * Replace text in specified range
   * @param contentState - Current content state
   * @param targetRange - Selection range to replace
   * @param text - Replacement text
   * @param inlineStyle - Optional inline styles to apply
   * @param entityKey - Optional entity key to apply
   * @returns New ContentState with text replaced
   */
  replaceText(
    contentState: ContentState,
    targetRange: SelectionState,
    text: string,
    inlineStyle?: DraftInlineStyle,
    entityKey?: ?string
  ): ContentState;
  
  /**
   * Insert text at specified range (collapses range first if needed)
   * @param contentState - Current content state
   * @param targetRange - Selection range for insertion
   * @param text - Text to insert
   * @param inlineStyle - Optional inline styles to apply
   * @param entityKey - Optional entity key to apply
   * @returns New ContentState with text inserted
   */
  insertText(
    contentState: ContentState,
    targetRange: SelectionState,
    text: string,
    inlineStyle?: DraftInlineStyle,
    entityKey?: ?string
  ): ContentState;
  
  /**
   * Move text from one location to another
   * @param contentState - Current content state
   * @param removalRange - Range of text to move
   * @param targetRange - Destination for moved text
   * @returns New ContentState with text moved
   */
  moveText(
    contentState: ContentState,
    removalRange: SelectionState,
    targetRange: SelectionState
  ): ContentState;
  
  /**
   * Replace range with content fragment
   * @param contentState - Current content state
   * @param targetRange - Range to replace
   * @param fragment - Content blocks to insert
   * @returns New ContentState with fragment inserted
   */
  replaceWithFragment(
    contentState: ContentState,
    targetRange: SelectionState,
    fragment: BlockMap
  ): ContentState;
  
  /**
   * Remove content in specified range
   * @param contentState - Current content state
   * @param targetRange - Range to remove
   * @param removalDirection - Direction of removal (forward/backward)
   * @returns New ContentState with content removed
   */
  removeRange(
    contentState: ContentState,
    targetRange: SelectionState,
    removalDirection: DraftRemovalDirection
  ): ContentState;
  
  /**
   * Split block at specified position
   * @param contentState - Current content state
   * @param targetRange - Position to split (should be collapsed)
   * @returns New ContentState with block split
   */
  splitBlock(
    contentState: ContentState,
    targetRange: SelectionState
  ): ContentState;
  
  /**
   * Apply inline style to range
   * @param contentState - Current content state
   * @param targetRange - Range to style
   * @param inlineStyle - Style name to apply
   * @returns New ContentState with style applied
   */
  applyInlineStyle(
    contentState: ContentState,
    targetRange: SelectionState,
    inlineStyle: string
  ): ContentState;
  
  /**
   * Remove inline style from range
   * @param contentState - Current content state
   * @param targetRange - Range to modify
   * @param inlineStyle - Style name to remove
   * @returns New ContentState with style removed
   */
  removeInlineStyle(
    contentState: ContentState,
    targetRange: SelectionState,
    inlineStyle: string
  ): ContentState;
  
  /**
   * Set block type for blocks in range
   * @param contentState - Current content state
   * @param targetRange - Range of blocks to modify
   * @param blockType - New block type
   * @returns New ContentState with block types changed
   */
  setBlockType(
    contentState: ContentState,
    targetRange: SelectionState,
    blockType: DraftBlockType
  ): ContentState;
  
  /**
   * Set block data for blocks in range
   * @param contentState - Current content state
   * @param targetRange - Range of blocks to modify
   * @param blockData - New block data
   * @returns New ContentState with block data set
   */
  setBlockData(
    contentState: ContentState,
    targetRange: SelectionState,
    blockData: Map<any, any>
  ): ContentState;
  
  /**
   * Merge block data for blocks in range
   * @param contentState - Current content state
   * @param targetRange - Range of blocks to modify
   * @param blockData - Block data to merge
   * @returns New ContentState with block data merged
   */
  mergeBlockData(
    contentState: ContentState,
    targetRange: SelectionState,
    blockData: Map<any, any>
  ): ContentState;
  
  /**
   * Apply entity to range
   * @param contentState - Current content state
   * @param targetRange - Range to apply entity
   * @param entityKey - Entity key to apply (null to remove)
   * @returns New ContentState with entity applied
   */
  applyEntity(
    contentState: ContentState,
    targetRange: SelectionState,
    entityKey: ?string
  ): ContentState;
};

Usage Examples:

import { Modifier, EditorState, SelectionState } from "draft-js";

function insertTextExample(editorState) {
  const contentState = editorState.getCurrentContent();
  const selection = editorState.getSelection();
  
  // Insert text with bold styling
  const newContentState = Modifier.insertText(
    contentState,
    selection,
    "Hello World!",
    OrderedSet(['BOLD'])
  );
  
  return EditorState.push(editorState, newContentState, 'insert-characters');
}

function applyStyleExample(editorState) {
  const contentState = editorState.getCurrentContent();
  const selection = editorState.getSelection();
  
  // Apply italic style to selected text
  const newContentState = Modifier.applyInlineStyle(
    contentState,
    selection,
    'ITALIC'
  );
  
  return EditorState.push(editorState, newContentState, 'change-inline-style');
}

function changeBlockTypeExample(editorState) {
  const contentState = editorState.getCurrentContent();
  const selection = editorState.getSelection();
  
  // Change current block to header
  const newContentState = Modifier.setBlockType(
    contentState,
    selection,
    'header-one'
  );
  
  return EditorState.push(editorState, newContentState, 'change-block-type');
}

RichTextEditorUtil (exported as RichUtils)

High-level utilities for common rich text editing operations. These functions work directly with EditorState.

/**
 * High-level rich text editing utilities
 */
const RichUtils: {
  /**
   * Handle keyboard commands (bold, italic, undo, etc.)
   * @param editorState - Current editor state
   * @param command - Command string from key binding
   * @returns New EditorState or null if not handled
   */
  handleKeyCommand(editorState: EditorState, command: DraftEditorCommand): ?EditorState;
  
  /**
   * Toggle inline style (add if not present, remove if present)
   * @param editorState - Current editor state
   * @param inlineStyle - Style name to toggle
   * @returns New EditorState with style toggled
   */
  toggleInlineStyle(editorState: EditorState, inlineStyle: string): EditorState;
  
  /**
   * Toggle block type
   * @param editorState - Current editor state
   * @param blockType - Block type to toggle to
   * @returns New EditorState with block type changed
   */
  toggleBlockType(editorState: EditorState, blockType: DraftBlockType): EditorState;
  
  /**
   * Toggle link entity on selection
   * @param editorState - Current editor state
   * @param targetSelection - Selection to apply link to
   * @param entityKey - Link entity key (null to remove link)
   * @returns New EditorState with link toggled
   */
  toggleLink(
    editorState: EditorState,
    targetSelection: SelectionState,
    entityKey: ?string
  ): EditorState;
  
  /**
   * Insert soft newline (line break within block)
   * @param editorState - Current editor state
   * @returns New EditorState with soft newline inserted
   */
  insertSoftNewline(editorState: EditorState): EditorState;
  
  /**
   * Handle backspace key press
   * @param editorState - Current editor state
   * @returns New EditorState or null if not handled
   */
  onBackspace(editorState: EditorState): ?EditorState;
  
  /**
   * Handle delete key press
   * @param editorState - Current editor state
   * @returns New EditorState or null if not handled  
   */
  onDelete(editorState: EditorState): ?EditorState;
  
  /**
   * Handle tab key press (for list indentation)
   * @param event - Keyboard event
   * @param editorState - Current editor state
   * @param maxDepth - Maximum nesting depth
   * @returns New EditorState with depth adjusted
   */
  onTab(
    event: SyntheticKeyboardEvent,
    editorState: EditorState,
    maxDepth: number
  ): EditorState;
  
  /**
   * Toggle code formatting
   * @param editorState - Current editor state
   * @returns New EditorState with code toggled
   */
  toggleCode(editorState: EditorState): EditorState;
  
  /**
   * Get current block type
   * @param editorState - Current editor state
   * @returns Block type of current block
   */
  getCurrentBlockType(editorState: EditorState): DraftBlockType;
  
  /**
   * Check if current block contains link
   * @param editorState - Current editor state
   * @returns True if current selection contains a link
   */
  currentBlockContainsLink(editorState: EditorState): boolean;
  
  /**
   * Try to remove block-level styling
   * @param editorState - Current editor state
   * @returns New ContentState or null if no change
   */
  tryToRemoveBlockStyle(editorState: EditorState): ?ContentState;
};

Usage Examples:

import { RichUtils, EditorState } from "draft-js";

// Handle keyboard commands
function handleKeyCommand(command, editorState) {
  const newState = RichUtils.handleKeyCommand(editorState, command);
  if (newState) {
    return newState;
  }
  return editorState;
}

// Toggle formatting
function toggleBold(editorState) {
  return RichUtils.toggleInlineStyle(editorState, 'BOLD');
}

function toggleItalic(editorState) {
  return RichUtils.toggleInlineStyle(editorState, 'ITALIC');
}

function toggleHeader(editorState) {
  return RichUtils.toggleBlockType(editorState, 'header-one');
}

// Check current state
function isCurrentBlockHeader(editorState) {
  const blockType = RichUtils.getCurrentBlockType(editorState);
  return blockType.startsWith('header-');
}

// Handle special keys
function handleTab(e, editorState) {
  const newState = RichUtils.onTab(e, editorState, 4);
  return newState;
}

function handleReturn(e, editorState) {
  if (e.shiftKey) {
    return RichUtils.insertSoftNewline(editorState);
  }
  return editorState;
}

AtomicBlockUtils

Utilities for working with atomic blocks - blocks that contain non-text content like images, videos, or embeds.

/**
 * Utilities for atomic blocks (media, embeds, etc.)
 */
const AtomicBlockUtils: {
  /**
   * Insert atomic block at current selection
   * @param editorState - Current editor state
   * @param entityKey - Entity key for the atomic content
   * @param character - Character to represent the atomic block
   * @returns New EditorState with atomic block inserted
   */
  insertAtomicBlock(
    editorState: EditorState,
    entityKey: string,
    character: string
  ): EditorState;
  
  /**
   * Move atomic block to new position
   * @param editorState - Current editor state
   * @param atomicBlock - The atomic block to move
   * @param targetRange - Destination selection
   * @param insertionMode - How to insert ('before' | 'after')
   * @returns New EditorState with atomic block moved
   */
  moveAtomicBlock(
    editorState: EditorState,
    atomicBlock: ContentBlock,
    targetRange: SelectionState,
    insertionMode?: DraftInsertionType
  ): EditorState;
};

Usage Examples:

import { AtomicBlockUtils, Entity, EditorState } from "draft-js";

// Insert image
function insertImage(editorState, src, alt) {
  // Create entity for the image
  const entityKey = Entity.create('IMAGE', 'IMMUTABLE', {
    src: src,
    alt: alt
  });
  
  // Insert atomic block
  return AtomicBlockUtils.insertAtomicBlock(
    editorState,
    entityKey,
    ' ' // Space character as placeholder
  );
}

// Insert video embed
function insertVideo(editorState, videoUrl) {
  const entityKey = Entity.create('VIDEO', 'IMMUTABLE', {
    src: videoUrl
  });
  
  return AtomicBlockUtils.insertAtomicBlock(
    editorState,
    entityKey,
    '📹' // Video emoji as placeholder
  );
}

// Move atomic block
function moveAtomicBlockUp(editorState, atomicBlock) {
  const selection = editorState.getSelection();
  const targetRange = selection.merge({
    anchorKey: atomicBlock.getKey(),
    focusKey: atomicBlock.getKey()
  });
  
  return AtomicBlockUtils.moveAtomicBlock(
    editorState,
    atomicBlock,
    targetRange,
    'before'
  );
}

Types

Modification-Related Types

// Removal direction
type DraftRemovalDirection = 'backward' | 'forward';

// Insertion type for atomic blocks
type DraftInsertionType = 'replace' | 'before' | 'after';

// Editor commands
type DraftEditorCommand = 'backspace' | 'backspace-word' | 'backspace-to-start-of-line' |
  'delete' | 'delete-word' | 'delete-to-end-of-block' | 'enter' | 'forward-delete' |
  'insert-characters' | 'insert-compose-character' | 'move-selection-to-start-of-block' |
  'move-selection-to-end-of-block' | 'secondary-cut' | 'secondary-paste' | 'split-block' |
  'transpose-characters' | 'undo' | 'redo' | 'bold' | 'code' | 'italic' | 'strikethrough' |
  'underline';

// Block merge behavior
type BlockDataMergeBehavior = 'REPLACE_WITH_NEW_DATA' | 'MERGE_OLD_DATA_TO_NEW_DATA';

// Entity mutability
type DraftEntityMutability = 'MUTABLE' | 'IMMUTABLE' | 'SEGMENTED';