fix(ios): TextField keyboard handling with emoji, autofill, and shortcuts (#10154)

closes https://github.com/NativeScript/NativeScript/issues/10108
This commit is contained in:
Nathan Walker
2023-01-03 17:36:56 -08:00
committed by GitHub
parent d138ac000d
commit 00944bb1b5
9 changed files with 50 additions and 4 deletions

View File

@ -10,6 +10,7 @@
<Button text="box-shadow" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
<Button text="css-playground" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
<Button text="datepicker" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
<Button text="forms" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
<Button text="image-async" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
<Button text="image-handling" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
<Button text="labels" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />

View File

@ -0,0 +1,16 @@
import { Page, Observable, EventData } from '@nativescript/core';
let page: Page;
export function navigatingTo(args: EventData) {
page = <Page>args.object;
page.bindingContext = new SampleData();
}
export class SampleData extends Observable {
textInput = '';
textChange(args) {
console.log(args.object.text);
this.notifyPropertyChange('textInput', args.object.text);
}
}

View File

@ -0,0 +1,15 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
<Page.actionBar>
<ActionBar title="Labels and TextView" class="action-bar">
</ActionBar>
</Page.actionBar>
<ScrollView>
<StackLayout padding="20">
<Label text="TextField:" fontWeight="bold" />
<TextField textChange="{{ textChange }}" marginTop="6" backgroundColor="#efefef" padding="8" fontSize="18" keyboardType="url" />
<Label text="{{ textInput }}" marginTop="6" />
</StackLayout>
</ScrollView>
</Page>

View File

@ -44,6 +44,7 @@
"css": "^3.0.0",
"css-tree": "^1.1.2",
"dotenv": "10.0.0",
"emoji-regex": "^10.2.1",
"eslint": "7.22.0",
"eslint-config-prettier": "^8.1.0",
"gonzales": "^1.0.7",

View File

@ -105,7 +105,7 @@ export type { InstrumentationMode, TimerInfo } from './profiling';
export { encoding } from './text';
export * from './trace';
export * from './ui';
import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, executeOnUIThread, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, escapeRegexSymbols, convertString, dismissSoftInput, dismissKeyboard, queueMacrotask, queueGC, throttle, debounce, dataSerialize, dataDeserialize, copyToClipboard, getFileExtension } from './utils';
import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, executeOnUIThread, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, escapeRegexSymbols, convertString, dismissSoftInput, dismissKeyboard, queueMacrotask, queueGC, throttle, debounce, dataSerialize, dataDeserialize, copyToClipboard, getFileExtension, isEmoji } from './utils';
import { SDK_VERSION } from './utils/constants';
import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback, numberHasDecimals, numberIs64Bit } from './utils/types';
export declare const Utils: {
@ -163,5 +163,6 @@ export declare const Utils: {
dismissSoftInput: typeof dismissSoftInput;
dismissKeyboard: typeof dismissKeyboard;
copyToClipboard: typeof copyToClipboard;
isEmoji: typeof isEmoji;
};
export { XmlParser, ParserEventType, ParserEvent } from './xml';

View File

@ -137,7 +137,7 @@ export * from './trace';
export * from './ui';
import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, executeOnUIThread, queueMacrotask, queueGC, debounce, throttle, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, RESOURCE_PREFIX, FILE_PREFIX, escapeRegexSymbols, convertString, dismissSoftInput, dismissKeyboard, dataDeserialize, dataSerialize, copyToClipboard, getFileExtension } from './utils';
import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, executeOnUIThread, queueMacrotask, queueGC, debounce, throttle, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, RESOURCE_PREFIX, FILE_PREFIX, escapeRegexSymbols, convertString, dismissSoftInput, dismissKeyboard, dataDeserialize, dataSerialize, copyToClipboard, getFileExtension, isEmoji } from './utils';
import { SDK_VERSION } from './utils/constants';
import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback, numberHasDecimals, numberIs64Bit } from './utils/types';
@ -199,6 +199,7 @@ export const Utils = {
dismissSoftInput,
dismissKeyboard,
copyToClipboard,
isEmoji,
};
export { XmlParser, ParserEventType, ParserEvent } from './xml';

View File

@ -47,6 +47,7 @@
"@nativescript/hook": "~2.0.0",
"acorn": "^8.7.0",
"css-tree": "^1.1.2",
"emoji-regex": "^10.2.1",
"reduce-css-calc": "^2.1.7",
"tslib": "^2.0.0"
},

View File

@ -4,7 +4,7 @@ import { hintProperty, placeholderColorProperty, _updateCharactersInRangeReplace
import { CoreTypes } from '../../core-types';
import { Color } from '../../color';
import { colorProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty } from '../styling/style-properties';
import { layout } from '../../utils';
import { layout, isEmoji } from '../../utils';
import { profile } from '../../profiling';
export * from './text-field-common';
@ -200,7 +200,10 @@ export class TextField extends TextFieldBase {
}
if (this.updateTextTrigger === 'textChanged') {
const shouldReplaceString = (textField.secureTextEntry && this.firstEdit) || delta > 1;
// 1. secureTextEntry with firstEdit should not replace
// 2. emoji's should not replace value
// 3. convenient keyboard shortcuts should not replace value (eg, '.com')
const shouldReplaceString = (textField.secureTextEntry && this.firstEdit) || (delta > 1 && !isEmoji(replacementString) && delta !== replacementString.length);
if (shouldReplaceString) {
textProperty.nativeValueChange(this, replacementString);
} else {

View File

@ -1,6 +1,7 @@
import * as types from './types';
import { dispatchToMainThread, dispatchToUIThread, isMainThread } from './mainthread-helper';
import { sanitizeModuleName } from '../ui/builder/module-name-sanitizer';
import emojiRegex from 'emoji-regex';
import { GC } from './index';
@ -203,3 +204,9 @@ export function queueGC(delay = 900, useThrottle?: boolean) {
debouncedGC.get(delay)();
}
}
export function isEmoji(value: string): boolean {
// TODO: In a future runtime update, we can switch to using Unicode Property Escapes:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes
return emojiRegex().test(value);
}