CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-number-format

React component library for formatting numbers in input fields and text display with sophisticated caret engine and customizable patterns.

Pending
Overview
Eval results
Files

react-hooks.mddocs/

React Hooks

React hooks for advanced use cases where you need to integrate formatting logic with custom components, complex state management, or non-standard UI patterns. These hooks provide the same formatting logic as the components but give you full control over rendering and behavior.

Capabilities

useNumericFormat Hook

React hook that provides numeric formatting logic for building custom components with full control over rendering and state management.

/**
 * Hook that provides numeric formatting logic for custom components
 * @param props - NumericFormat configuration options
 * @returns Props configured for NumberFormatBase component
 */
function useNumericFormat<BaseType = InputAttributes>(
  props: NumericFormatProps<BaseType>
): NumberFormatBaseProps<BaseType>;

Usage Examples:

import React, { useState } from "react";
import { useNumericFormat, NumberFormatBase } from "react-number-format";

// Custom currency input with validation styling
function CurrencyInputWithValidation({ value, onChange, min = 0, max = 1000000 }) {
  const [isValid, setIsValid] = useState(true);
  
  const numericProps = useNumericFormat({
    value,
    thousandSeparator: true,
    prefix: "$",
    decimalScale: 2,
    fixedDecimalScale: true,
    onValueChange: (values) => {
      const amount = values.floatValue || 0;
      const valid = amount >= min && amount <= max;
      setIsValid(valid);
      onChange(values.floatValue);
    },
    isAllowed: (values) => {
      const amount = values.floatValue;
      return amount === undefined || (amount >= min && amount <= max);
    }
  });

  return (
    <div className={`currency-input-wrapper ${isValid ? 'valid' : 'invalid'}`}>
      <NumberFormatBase
        {...numericProps}
        customInput={(props) => (
          <input 
            {...props}
            className={`currency-input ${isValid ? 'border-green' : 'border-red'}`}
          />
        )}
      />
      {!isValid && (
        <div className="error-message">
          Amount must be between ${min.toLocaleString()} and ${max.toLocaleString()}
        </div>
      )}
    </div>
  );
}

// Multi-currency input with dynamic symbols
function MultiCurrencyInput({ value, currency, onValueChange, onCurrencyChange }) {
  const currencyConfig = {
    USD: { prefix: '$', decimalSeparator: '.', thousandSeparator: ',' },
    EUR: { prefix: '€', decimalSeparator: ',', thousandSeparator: '.' },
    GBP: { prefix: '£', decimalSeparator: '.', thousandSeparator: ',' },
    JPY: { prefix: '¥', decimalScale: 0, thousandSeparator: ',' }
  };

  const config = currencyConfig[currency] || currencyConfig.USD;
  
  const numericProps = useNumericFormat({
    value,
    ...config,
    onValueChange: (values) => onValueChange(values.floatValue)
  });

  return (
    <div className="multi-currency-input">
      <select 
        value={currency} 
        onChange={(e) => onCurrencyChange(e.target.value)}
        className="currency-selector"
      >
        {Object.keys(currencyConfig).map(curr => (
          <option key={curr} value={curr}>{curr}</option>
        ))}
      </select>
      <NumberFormatBase {...numericProps} />
    </div>
  );
}

// Percentage input with slider integration
function PercentageSliderInput({ value, onChange, min = 0, max = 100 }) {
  const numericProps = useNumericFormat({
    value,
    suffix: '%',
    decimalScale: 1,
    allowNegative: false,
    onValueChange: (values) => onChange(values.floatValue || 0),
    isAllowed: (values) => {
      const val = values.floatValue;
      return val === undefined || (val >= min && val <= max);
    }
  });

  return (
    <div className="percentage-input-group">
      <input
        type="range"
        min={min}
        max={max}
        value={value || 0}
        onChange={(e) => onChange(parseFloat(e.target.value))}
        className="percentage-slider"
      />
      <NumberFormatBase 
        {...numericProps}
        customInput={(props) => (
          <input {...props} className="percentage-text-input" />
        )}
      />
    </div>
  );
}

usePatternFormat Hook

React hook that provides pattern formatting logic for building custom components with masking and structured input.

/**
 * Hook that provides pattern formatting logic for custom components  
 * @param props - PatternFormat configuration options
 * @returns Props configured for NumberFormatBase component
 */
function usePatternFormat<BaseType = InputAttributes>(
  props: PatternFormatProps<BaseType>
): NumberFormatBaseProps<BaseType>;

Usage Examples:

import React, { useState, useEffect } from "react";
import { usePatternFormat, NumberFormatBase } from "react-number-format";

// Phone input with country code detection
function InternationalPhoneInput({ value, onChange, onCountryChange }) {
  const [country, setCountry] = useState('US');
  
  const patterns = {
    US: { format: "(###) ###-####", mask: "_" },
    UK: { format: "+44 #### ######", mask: "_" },
    IN: { format: "+91 ##### #####", mask: "_" },
    DE: { format: "+49 ### #### ####", mask: "_" }
  };

  // Detect country from input
  useEffect(() => {
    if (value?.startsWith('+44')) setCountry('UK');
    else if (value?.startsWith('+91')) setCountry('IN');
    else if (value?.startsWith('+49')) setCountry('DE');
    else setCountry('US');
  }, [value]);

  const patternProps = usePatternFormat({
    ...patterns[country],
    value,
    onValueChange: (values) => {
      onChange(values.value);
      onCountryChange?.(country);
    }
  });

  return (
    <div className="international-phone-input">
      <select 
        value={country}
        onChange={(e) => {
          setCountry(e.target.value);
          onChange(''); // Clear input when country changes
        }}
        className="country-selector"
      >
        <option value="US">🇺🇸 US</option>
        <option value="UK">🇬🇧 UK</option>
        <option value="IN">🇮🇳 India</option>
        <option value="DE">🇩🇪 Germany</option>
      </select>
      <NumberFormatBase {...patternProps} />
    </div>
  );
}

// Credit card input with type detection and validation
function CreditCardInput({ value, onChange, onCardTypeChange }) {
  const [cardType, setCardType] = useState('unknown');
  
  const detectCardType = (number: string) => {
    const patterns = {
      visa: /^4/,
      mastercard: /^5[1-5]/,
      amex: /^3[47]/,
      discover: /^6(?:011|5)/
    };
    
    for (const [type, pattern] of Object.entries(patterns)) {
      if (pattern.test(number)) return type;
    }
    return 'unknown';
  };

  const getPattern = (type: string) => {
    switch (type) {
      case 'amex': return "#### ###### #####";
      default: return "#### #### #### ####";
    }
  };

  const patternProps = usePatternFormat({
    format: getPattern(cardType),
    mask: "_",
    value,
    onValueChange: (values) => {
      const type = detectCardType(values.value);
      setCardType(type);
      onCardTypeChange?.(type);
      onChange(values.value);
    }
  });

  return (
    <div className="credit-card-input">
      <div className="card-type-indicator">
        <span className={`card-icon ${cardType}`}>
          {cardType.toUpperCase()}
        </span>
      </div>
      <NumberFormatBase 
        {...patternProps}
        customInput={(props) => (
          <input 
            {...props} 
            className={`card-input ${cardType}`}
            placeholder={getPattern(cardType).replace(/#/g, '0')}
          />
        )}
      />
    </div>
  );
}

// Date input with validation and calendar integration
function DateInput({ value, onChange, minDate, maxDate }) {
  const [isValid, setIsValid] = useState(true);

  const validateDate = (dateString: string) => {
    if (dateString.length !== 8) return false;
    
    const month = parseInt(dateString.substring(0, 2));
    const day = parseInt(dateString.substring(2, 4));  
    const year = parseInt(dateString.substring(4, 8));
    
    const date = new Date(year, month - 1, day);
    const isValidDate = date.getMonth() === month - 1 && 
                       date.getDate() === day && 
                       date.getFullYear() === year;
    
    if (!isValidDate) return false;
    
    if (minDate && date < minDate) return false;
    if (maxDate && date > maxDate) return false;
    
    return true;
  };

  const patternProps = usePatternFormat({
    format: "##/##/####",
    mask: ['M', 'M', 'D', 'D', 'Y', 'Y', 'Y', 'Y'],
    value,
    allowEmptyFormatting: true,
    onValueChange: (values) => {
      const valid = values.value.length === 0 || validateDate(values.value);
      setIsValid(valid);
      onChange(values.value);
    }
  });

  return (
    <div className={`date-input-wrapper ${isValid ? 'valid' : 'invalid'}`}>
      <NumberFormatBase 
        {...patternProps}
        customInput={(props) => (
          <input 
            {...props}
            className={`date-input ${isValid ? '' : 'error'}`}
            placeholder="MM/DD/YYYY"
          />
        )}
      />
      {!isValid && (
        <div className="error-message">Please enter a valid date</div>
      )}
    </div>
  );
}

Advanced Hook Patterns

Combining Multiple Formatters

// Component that switches between numeric and pattern formatting
function DynamicFormattedInput({ type, value, onChange }) {
  const numericProps = useNumericFormat({
    value: type === 'currency' ? value : '',
    thousandSeparator: true,
    prefix: '$',
    decimalScale: 2,
    onValueChange: (values) => onChange(values.floatValue)
  });

  const phoneProps = usePatternFormat({
    format: "(###) ###-####",
    mask: "_",
    value: type === 'phone' ? value : '',
    onValueChange: (values) => onChange(values.value)
  });

  const ssnProps = usePatternFormat({
    format: "###-##-####", 
    mask: "_",
    value: type === 'ssn' ? value : '',
    onValueChange: (values) => onChange(values.value)
  });

  const getProps = () => {
    switch (type) {
      case 'currency': return numericProps;
      case 'phone': return phoneProps;
      case 'ssn': return ssnProps;
      default: return { value, onValueChange: (v) => onChange(v.value) };
    }
  };

  return (
    <div className="dynamic-input">
      <select value={type} onChange={(e) => onChange('')}>
        <option value="text">Text</option>
        <option value="currency">Currency</option>
        <option value="phone">Phone</option>
        <option value="ssn">SSN</option>
      </select>
      <NumberFormatBase {...getProps()} />
    </div>
  );
}

State Management Integration

// Integration with Redux or other state managers
function ConnectedCurrencyInput({ value, dispatch, fieldName }) {
  const numericProps = useNumericFormat({
    value,
    thousandSeparator: true,
    prefix: '$',
    decimalScale: 2,
    fixedDecimalScale: true,
    onValueChange: (values) => {
      dispatch({
        type: 'UPDATE_FIELD',
        payload: {
          field: fieldName,
          value: values.floatValue,
          formattedValue: values.formattedValue,
          isValid: values.floatValue !== undefined
        }
      });
    }
  });

  return <NumberFormatBase {...numericProps} />;
}

Custom Validation Hooks

// Hook that combines formatting with validation
function useValidatedNumericFormat(props, validator) {
  const [error, setError] = useState(null);
  
  const numericProps = useNumericFormat({
    ...props,
    onValueChange: (values, sourceInfo) => {
      const validationResult = validator(values);
      setError(validationResult.error);
      
      if (props.onValueChange) {
        props.onValueChange(values, sourceInfo);
      }
    }
  });

  return { numericProps, error };
}

// Usage
function ValidatedCurrencyInput({ value, onChange }) {
  const validator = (values) => {
    const amount = values.floatValue || 0;
    if (amount > 10000) {
      return { error: 'Amount cannot exceed $10,000' };
    }
    return { error: null };
  };

  const { numericProps, error } = useValidatedNumericFormat({
    value,
    thousandSeparator: true,
    prefix: '$',
    onValueChange: (values) => onChange(values.floatValue)
  }, validator);

  return (
    <div>
      <NumberFormatBase {...numericProps} />
      {error && <div className="error">{error}</div>}
    </div>
  );
}

Install with Tessl CLI

npx tessl i tessl/npm-react-number-format

docs

base-component.md

index.md

numeric-formatting.md

pattern-formatting.md

react-hooks.md

utility-functions.md

tile.json