From 38a90db3e46761b2214fa93637635b59ec7c72bb Mon Sep 17 00:00:00 2001 From: kirillzyusko Date: Tue, 4 Feb 2025 17:59:04 +0100 Subject: [PATCH] feat: transform props (basic POC, web works) --- README.md | 2 +- apps/example/src/screens/Main/index.tsx | 19 +++++++++-- ...vancedTextInputMaskDecoratorViewManager.kt | 8 ++--- ...xtInputMaskDecoratorViewNativeComponent.ts | 8 ++--- .../native/views/MaskedTextInput/index.tsx | 4 +-- package/src/types.ts | 12 +++---- .../src/web/AdvancedTextInputMaskListener.ts | 34 ++++++++++++++----- .../hooks/useMaskedTextInputListener/index.ts | 4 ++- .../src/web/views/MaskedTextInput/index.tsx | 2 ++ 9 files changed, 64 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index bc8ab5b..5a6334b 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ For applications requiring conditional or more complex formatting, this package | `autoSkip` | `boolean` | Automatically skips to the next input field when the current one is filled. Default is `false`. | | `isRTL` | `boolean` | Enables right-to-left (RTL) text direction for the text input. Default is `false`. | | `affinityCalculationStrategy` | `AFFINITY_CALCULATION_STRATEGY` | Defines the strategy for affinity calculation, determining how the best mask format is selected based on input. | -| `customTransformation` | `CustomTransformation` | Custom transformation applied to the text input to define how the input text should be transformed. | +| `transformation` | `Transformation` | A transformation applied to the text input to define which symbols should be transformed in which symbols. | | `defaultValue` | `string` | The default value for the input field. | | `value` | `string` | Current value of the input field, allowing controlled input behavior. | | `allowSuggestions` | `boolean` (iOS only) | Enables or disables input suggestions on iOS. Default is `false`. | diff --git a/apps/example/src/screens/Main/index.tsx b/apps/example/src/screens/Main/index.tsx index da31912..b87da00 100644 --- a/apps/example/src/screens/Main/index.tsx +++ b/apps/example/src/screens/Main/index.tsx @@ -23,8 +23,8 @@ const Main = () => { const inputRef = React.useRef(null); const [textState, setTextState] = React.useState({ - extracted: '0000', - formatted: '00-00', + extracted: '', + formatted: '', }); const [focused, setFocused] = React.useState(false); @@ -79,7 +79,20 @@ const Main = () => { style={styles.maskedTextInput} onChangeText={onChangeText} onTailPlaceholderChange={console.log} - mask="[00]-[$$]-[00]" + mask="[0999999].[09]" + // - mom, can we have a neural network at home? + // - we already have a neural network at home. + // neural network at home: + affinityFormat={[ + '[0].[00]', + '[00].[00]', + '[000].[00]', + '[0000].[00]', + '[00000].[00]', + '[000000].[00]', + '[0000000].[00]', + ]} + transformation={[{ set: ',', to: '.' }]} autocomplete={false} allowSuggestions={true} autocompleteOnFocus={false} diff --git a/package/android/src/main/java/com/maskedtextinput/managers/AdvancedTextInputMaskDecoratorViewManager.kt b/package/android/src/main/java/com/maskedtextinput/managers/AdvancedTextInputMaskDecoratorViewManager.kt index 605dc01..9dc3316 100644 --- a/package/android/src/main/java/com/maskedtextinput/managers/AdvancedTextInputMaskDecoratorViewManager.kt +++ b/package/android/src/main/java/com/maskedtextinput/managers/AdvancedTextInputMaskDecoratorViewManager.kt @@ -105,12 +105,12 @@ class AdvancedTextInputMaskDecoratorViewManager( view.setAutoComplete(autocomplete) } - @ReactProp(name = "customTransformation") - override fun setCustomTransformation( + @ReactProp(name = "transformation") + override fun setTransformation( view: AdvancedTextInputMaskDecoratorView, - customTransformation: ReadableMap?, + transformation: ReadableMap?, ) { - view.setCustomTransformationMethod(customTransformation) + view.setTransformationMethod(transformation) } @ReactProp(name = "allowedKeys") diff --git a/package/src/native/specs/AdvancedTextInputMaskDecoratorViewNativeComponent.ts b/package/src/native/specs/AdvancedTextInputMaskDecoratorViewNativeComponent.ts index cddde9e..9136085 100644 --- a/package/src/native/specs/AdvancedTextInputMaskDecoratorViewNativeComponent.ts +++ b/package/src/native/specs/AdvancedTextInputMaskDecoratorViewNativeComponent.ts @@ -12,9 +12,9 @@ export type OnAdvancedMaskTextChange = Readonly<{ tailPlaceholder: string; }>; -type CustomTransformation = { - transformationChar: string; - transformationString: string; +type Transformation = { + set: string; + to: string; }; type Notation = { @@ -40,7 +40,7 @@ export interface NativeProps extends ViewProps { autocompleteOnFocus?: boolean; autoSkip?: boolean; customNotations?: ReadonlyArray; - customTransformation?: Readonly; + transformation?: Readonly; allowedKeys?: string; defaultValue?: string; isRTL?: boolean; diff --git a/package/src/native/views/MaskedTextInput/index.tsx b/package/src/native/views/MaskedTextInput/index.tsx index d93b71b..593181e 100644 --- a/package/src/native/views/MaskedTextInput/index.tsx +++ b/package/src/native/views/MaskedTextInput/index.tsx @@ -24,7 +24,7 @@ const MaskedTextInput = forwardRef( autocompleteOnFocus, autoSkip, customNotations, - customTransformation, + transformation, allowedKeys, defaultValue, isRTL, @@ -58,7 +58,7 @@ const MaskedTextInput = forwardRef( autocompleteOnFocus={autocompleteOnFocus} autoSkip={autoSkip} customNotations={customNotations} - customTransformation={customTransformation} + transformation={transformation} allowedKeys={allowedKeys} defaultValue={defaultValue} isRTL={isRTL} diff --git a/package/src/types.ts b/package/src/types.ts index 21a7c6e..53500f5 100644 --- a/package/src/types.ts +++ b/package/src/types.ts @@ -16,9 +16,9 @@ export type Notation = { isOptional: boolean; }; -type CustomTransformation = { - transformationChar: string; - transformationString: string; +export type Transformation = { + set: string; + to: string; }; export type MaskedTextInputOwnProps = { @@ -72,10 +72,10 @@ export type MaskedTextInputOwnProps = { */ affinityCalculationStrategy?: AFFINITY_CALCULATION_STRATEGY; /** - * Custom transformation to be applied to the text input. - * Defines how the input text should be transformed. + * Transformation to be applied to the text input. + * Defines how the inputted characters should be transformed. */ - customTransformation?: CustomTransformation; + transformation?: Transformation[]; /** * A string representing all symbols that can be entered in the text input. * For example: "0123456789". diff --git a/package/src/web/AdvancedTextInputMaskListener.ts b/package/src/web/AdvancedTextInputMaskListener.ts index 5e2b646..aa47b7a 100644 --- a/package/src/web/AdvancedTextInputMaskListener.ts +++ b/package/src/web/AdvancedTextInputMaskListener.ts @@ -2,7 +2,7 @@ import Mask from './helper/Mask'; import RTLMask from './helper/RTLMask'; import CaretString from './model/CaretString'; import { CaretGravityType } from './model/types'; -import type { Notation } from '../types'; +import type { Notation, Transformation } from '../types'; import type { MaskResult } from './model/types'; import { AFFINITY_CALCULATION_STRATEGY } from '../enums'; import { calculateAffinityOfMask } from './helper/affinityCalculationStrategy'; @@ -26,6 +26,7 @@ class MaskedTextChangedListener { public rightToLeft: boolean; public textField: Field | null = null; public allowedKeys: string; + public transformation: Transformation[]; private afterText: string = ''; @@ -37,7 +38,8 @@ class MaskedTextChangedListener { autocomplete: boolean = true, autoskip: boolean = false, rightToLeft: boolean = false, - allowedKeys: string = '' + allowedKeys: string = '', + transformation: Transformation[] = [] ) { this.primaryFormat = primaryFormat; this.affineFormats = affineFormats; @@ -47,6 +49,7 @@ class MaskedTextChangedListener { this.autoskip = autoskip; this.rightToLeft = rightToLeft; this.allowedKeys = allowedKeys; + this.transformation = transformation; } public get primaryMask(): Mask { @@ -58,9 +61,7 @@ class MaskedTextChangedListener { return; } - const newText = this.allowedKeys - ? [...text].filter((char) => this.allowedKeys.includes(char)).join('') - : text; + const newText = this.prepareText(text); const useAutocomplete = autocomplete ?? this.autocomplete; const textAndCaret = new CaretString(newText, newText.length, { @@ -112,6 +113,25 @@ class MaskedTextChangedListener { this.textField = textField; }; + /** + * Exclude undesired characters and apply `transformation` to the text. + */ + public prepareText = (text: string) => { + return ( + [...text] + // filter out characters not in allowedKeys (if allowedKeys is defined) + .filter((char) => !this.allowedKeys || this.allowedKeys.includes(char)) + // replace each character according to our transformation + .map((char) => { + const match = this.transformation.find(({ set }) => + set.includes(char) + ); + return match ? match.to : char; + }) + .join('') + ); + }; + public handleTextChange = ( event: NativeSyntheticEvent ): MaskResult => { @@ -135,9 +155,7 @@ class MaskedTextChangedListener { autoskip: useAutoskip, autocomplete: useAutocomplete, }; - const newText = this.allowedKeys - ? [...text].filter((char) => this.allowedKeys.includes(char)).join('') - : text; + const newText = this.prepareText(text); const textAndCaret = new CaretString(newText, caretPosition, caretGravity); const mask = this.pickMask(textAndCaret); const result = mask.apply(textAndCaret); diff --git a/package/src/web/hooks/useMaskedTextInputListener/index.ts b/package/src/web/hooks/useMaskedTextInputListener/index.ts index 9c90ac5..b64b41d 100644 --- a/package/src/web/hooks/useMaskedTextInputListener/index.ts +++ b/package/src/web/hooks/useMaskedTextInputListener/index.ts @@ -15,6 +15,7 @@ const useMaskedTextInputListener = ({ affinityCalculationStrategy, customNotations, allowedKeys = '', + transformation = [], autocomplete = true, autoSkip = false, isRTL = false, @@ -41,7 +42,8 @@ const useMaskedTextInputListener = ({ autocomplete, autoSkip, isRTL, - allowedKeys + allowedKeys, + transformation ) ); diff --git a/package/src/web/views/MaskedTextInput/index.tsx b/package/src/web/views/MaskedTextInput/index.tsx index f49a0cd..b6d2428 100644 --- a/package/src/web/views/MaskedTextInput/index.tsx +++ b/package/src/web/views/MaskedTextInput/index.tsx @@ -14,6 +14,7 @@ const MaskedTextInput = forwardRef( isRTL = false, mask, autoCapitalize = 'words', + transformation, allowedKeys, defaultValue, onChange, @@ -37,6 +38,7 @@ const MaskedTextInput = forwardRef( affinityCalculationStrategy, customNotations, allowedKeys, + transformation, autocomplete, autoSkip, isRTL,