TextSelection

Reactive tracking of text selection using the Selection API. Track what text the user has selected.

Loading demo...

Usage

angular-ts
import { Component } from '@angular/core';
import { textSelection } from '@signality/core';

@Component({
  template: `
    <p>Select some text in this paragraph to see it reflected below.</p>
    <p>Selected: "{{ selection.text() }}"</p>
  `,
})
export class SelectionDemo {
  readonly selection = textSelection(); 
}

Use InjectionToken for Singleton

For global state tracking like textSelection, consider using the provided TEXT_SELECTION token instead of calling the function directly. This provides a singleton instance that can be shared across your entire application, reducing memory usage and event listener overhead:

typescript
import { inject } from '@angular/core';
import { TEXT_SELECTION } from '@signality/core';

const selection = textSelection(); 
const selection = inject(TEXT_SELECTION); 

Learn more about Token-based utilities.

Parameters

ParameterTypeDescription
optionsTextSelectionOptionsOptional configuration (see Options below)

Options

The TextSelectionOptions extends WithInjector:

OptionTypeDescription
injectorInjectorOptional injector for DI context

Return Value

The textSelection() function returns a TextSelectionRef object:

PropertyTypeDescription
textSignal<string>The selected text content
rangesSignal<Range[]>Array of Range objects
rectsSignal<DOMRect[]>Bounding rectangles of selection (see DOMRect)
selectionSignal<Selection | null>The raw Selection object
clear() => voidClear the current text selection

Examples

Word counter

angular-ts
import { Component, computed } from '@angular/core';
import { textSelection } from '@signality/core';

@Component({
  template: `
    <textarea>
      Type or paste text here, then select portions to count.
    </textarea>
    
    <div class="stats">
      @if (hasSelection()) {
        <p>Selected: {{ wordCount() }} words, {{ charCount() }} characters</p>
      }
    </div>
  `,
})
export class SelectionCounter {
  readonly selection = textSelection();
  
  readonly charCount = computed(() => this.selection.text().length);
  readonly hasSelection = computed(() => this.charCount() > 0);
  
  readonly wordCount = computed(() => {
    const text = this.selection.text().trim();
    if (!text) return 0;
    return text.split(/\s+/).length;
  });
}

Quote selection

angular-ts
import { Component, computed, signal } from '@angular/core';
import { textSelection } from '@signality/core';

interface Quote {
  text: string;
  savedAt: number;
}

@Component({
  template: `
    <article>
      <p>Select text to save as a quote...</p>
    </article>
    
    @if (selection.text()) {
      <button (click)="saveQuote()">Save Quote</button>
    }
    
    <div class="saved-quotes">
      <h3>Saved Quotes</h3>
      @for (quote of quotes(); track quote.savedAt) {
        <blockquote>{{ quote.text }}</blockquote>
      }
    </div>
  `,
})
export class QuoteCollector {
  readonly selection = textSelection();
  readonly quotes = signal<Quote[]>([]);
  
  saveQuote() {
    const text = this.selection.text().trim();
    if (text) {
      this.quotes.update(quotes => [
        ...quotes,
        { text, savedAt: Date.now() }
      ]);
      
      this.selection.clear(); 
    }
  }
}

SSR Compatibility

On the server, signals initialize with safe defaults:

  • text()''
  • ranges()[]
  • rects()[]
  • selection()null
  • clear() → no-op function

Type Definitions

typescript
type TextSelectionOptions = WithInjector;

interface TextSelectionRef {
  readonly text: Signal<string>;
  readonly ranges: Signal<Range[]>;
  readonly rects: Signal<DOMRect[]>;
  readonly selection: Signal<Selection | null>;
  readonly clear: () => void;
}

function textSelection(options?: TextSelectionOptions): TextSelectionRef;
Edit this page on GitHub Last updated: Mar 19, 2026, 23:28:23