import React, { Component, type JSX } from 'react'; import AutoCompleteStatus from '@digigov/react-core/AutoCompleteStatus'; import AutoCompleteStatusContainer from '@digigov/react-core/AutoCompleteStatusContainer'; const debounce = function ( func: () => void, wait: number, immediate?: boolean ) { let timeout; return function (...args) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-this-alias const context = this; const later = function () { timeout = null; if (!immediate) func.apply(context, args); }; const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); return timeout; }; }; const statusDebounceMillis = 1400; interface StatusProps { id: string; length: number; queryLength: number; minQueryLength?: number; selectedOption: string; selectedOptionIndex: number; validChoiceMade: boolean; isInFocus: boolean; tQueryTooShort?: (x: number) => string; tNoResults?: () => string; tSelectedOption: (x: string, y: number, z: number) => string; tResults?: (x: number, y: string) => string; } interface StateProps { bump: boolean; debounced: boolean; silenced: boolean; } export default class Status extends Component { static defaultProps = { tQueryTooShort: (minQueryLength: number): string => `Type in ${minQueryLength} or more characters for results`, tNoResults: (): string => 'No search results', tSelectedOption: ( selectedOption: string, length: number, index: number ): string => `${selectedOption} ${index + 1} of ${length} is highlighted`, tResults: (length: number, contentSelectedOption: string): string => { const words = { result: length === 1 ? 'result' : 'results', is: length === 1 ? 'is' : 'are', }; return `${length} ${words.result} ${words.is} available. ${contentSelectedOption}`; }, }; state = { bump: false, debounced: false, silenced: false, }; debounceStatusUpdate: () => void; lastDebounceCall: any; componentWillUnmount(): void { clearTimeout(this.lastDebounceCall); } UNSAFE_componentWillMount() { // eslint-disable-next-line @typescript-eslint/no-this-alias const that = this; this.debounceStatusUpdate = debounce(function () { if (!that.state.debounced) { const shouldSilence = !that.props.isInFocus || that.props.validChoiceMade; that.setState(({ bump }) => ({ bump: !bump, debounced: true, silenced: shouldSilence, })); } }, statusDebounceMillis); } UNSAFE_componentWillReceiveProps() { this.setState({ debounced: false }); } render(): JSX.Element { const { id, length, queryLength, minQueryLength, selectedOption, selectedOptionIndex, tQueryTooShort, tNoResults, tSelectedOption, tResults, } = this.props; const { bump, debounced, silenced } = this.state; const queryTooShort = queryLength < (minQueryLength as number); const noResults = length === 0; const contentSelectedOption = selectedOption ? tSelectedOption(selectedOption, length, selectedOptionIndex) : ''; let content; if (queryTooShort) { content = tQueryTooShort?.(minQueryLength as number); } else if (noResults) { content = tNoResults?.(); } else { content = tResults?.(length, contentSelectedOption); } this.lastDebounceCall = this.debounceStatusUpdate(); return ( {!silenced && debounced && bump ? content : ' '} {!silenced && debounced && !bump ? content : ' '} ); } } export { AutoCompleteStatusContainer, AutoCompleteStatus };