UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

67 lines (60 loc) 1.97 kB
/** * Shared character counting functionality for text inputs with character limits. * Handles real-time character count updates, validation, and aria-live announcements. */ const SCREEN_READER_DELAY = 500; class CharacterCounter { announceTimeout = null; isInitialLoad = true; constructor(callbacks) { this.callbacks = callbacks; } /** * Update the character count based on current input value */ updateCharacterCount(currentLength, maxLength) { const charactersRemaining = maxLength - currentLength; // eslint-disable-next-line no-useless-assignment let message = ''; if (charactersRemaining >= 0) { const characterText = charactersRemaining === 1 ? 'character' : 'characters'; message = `${charactersRemaining} ${characterText} remaining`; this.callbacks.onCountUpdate(charactersRemaining, false, message); } else { const charactersOver = -charactersRemaining; const characterText = charactersOver === 1 ? 'character' : 'characters'; message = `${charactersOver} ${characterText} over`; this.callbacks.onCountUpdate(charactersOver, true, message); } if (!this.isInitialLoad) { this.announceToScreenReader(message); } // After first update, set isInitialLoad to false if (this.isInitialLoad) { this.isInitialLoad = false; } } /** * Announce character count to screen readers with debouncing */ announceToScreenReader(message) { if (this.announceTimeout) { clearTimeout(this.announceTimeout); } if (typeof window === 'undefined' || typeof window.setTimeout !== 'function') { return; } this.announceTimeout = window.setTimeout(() => { this.callbacks.onScreenReaderAnnounce(message); }, SCREEN_READER_DELAY); } /** * Clean up any pending timeouts */ cleanup() { if (this.announceTimeout) { clearTimeout(this.announceTimeout); } } } export { CharacterCounter };