Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | 53x 53x 53x 53x 53x 53x 53x 80x 107x 80x 80x 80x 80x 80x 53x 27x 53x 107x 107x 80x 9x 9x 9x 18x 9x 9x 1x 8x 8x 8x 8x 8x 8x 53x 53x | import creditCardType from 'credit-card-type'; import { FieldProps } from 'formik'; import { max } from 'lodash'; import React, { createRef, memo, useCallback, useMemo, ChangeEventHandler, Fragment, FunctionComponent, PureComponent, ReactNode, RefObject } from 'react'; import { TranslatedString } from '../../locale'; import { FormField, TextInput } from '../../ui/form'; import { IconLock } from '../../ui/icon'; import formatCreditCardNumber from './formatCreditCardNumber'; export interface CreditCardNumberFieldProps { name: string; } const CreditCardNumberField: FunctionComponent<CreditCardNumberFieldProps> = ({ name }) => { const renderInput = useCallback(({ field, form }: FieldProps<string>) => ( <CreditCardNumberInput field={ field } form={ form } /> ), []); const labelContent = useMemo(() => ( <TranslatedString id="payment.credit_card_number_label" /> ), []); return <FormField additionalClassName="form-field--ccNumber" input={ renderInput } labelContent={ labelContent } name={ name } />; }; class CreditCardNumberInput extends PureComponent<FieldProps<string>> { private inputRef: RefObject<HTMLInputElement> = createRef(); private nextSelectionEnd: number = 0; componentDidUpdate(): void { Iif (this.inputRef.current && this.inputRef.current.selectionEnd !== this.nextSelectionEnd) { this.inputRef.current.setSelectionRange(this.nextSelectionEnd, this.nextSelectionEnd); } } render(): ReactNode { const { field } = this.props; return ( <Fragment> <TextInput { ...field } additionalClassName="has-icon" autoComplete="cc-number" id={ field.name } onChange={ this.handleChange } ref={ this.inputRef } type="tel" /> <IconLock /> </Fragment> ); } private handleChange: ChangeEventHandler<HTMLInputElement> = event => { const separator = ' '; const { value = '' } = event.target; const { field, form } = this.props; const { name, value: previousValue = '' } = field; const selectionEnd = this.inputRef.current && this.inputRef.current.selectionEnd; // Only allow digits and spaces if (new RegExp(`[^\\d${separator}]`).test(value)) { return form.setFieldValue(name, previousValue); } const maxLength = max( creditCardType(value) .map(info => max(info.lengths)) ); const formattedValue = formatCreditCardNumber( value.replace(new RegExp(separator, 'g'), '').slice(0, maxLength), separator ); Iif (selectionEnd === value.length && value.length < formattedValue.length) { this.nextSelectionEnd = formattedValue.length; } else { this.nextSelectionEnd = selectionEnd || 0; } form.setFieldValue(name, formattedValue); }; } export default memo(CreditCardNumberField); |