mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 18:12:09 +08:00
fix(ios): TextField keyboard handling with emoji, autofill, and shortcuts (#10154)
closes https://github.com/NativeScript/NativeScript/issues/10108
This commit is contained in:
@ -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" />
|
||||
|
16
apps/toolbox/src/pages/forms.ts
Normal file
16
apps/toolbox/src/pages/forms.ts
Normal 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);
|
||||
}
|
||||
}
|
15
apps/toolbox/src/pages/forms.xml
Normal file
15
apps/toolbox/src/pages/forms.xml
Normal 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>
|
@ -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",
|
||||
|
3
packages/core/index.d.ts
vendored
3
packages/core/index.d.ts
vendored
@ -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';
|
||||
|
@ -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';
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user