Skip to content

Commit 00944bb

Browse files
authored
fix(ios): TextField keyboard handling with emoji, autofill, and shortcuts (#10154)
closes #10108
1 parent d138ac0 commit 00944bb

File tree

9 files changed

+50
-4
lines changed

9 files changed

+50
-4
lines changed

apps/toolbox/src/main-page.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<Button text="box-shadow" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
1111
<Button text="css-playground" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
1212
<Button text="datepicker" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
13+
<Button text="forms" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
1314
<Button text="image-async" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
1415
<Button text="image-handling" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />
1516
<Button text="labels" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo" />

apps/toolbox/src/pages/forms.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Page, Observable, EventData } from '@nativescript/core';
2+
3+
let page: Page;
4+
5+
export function navigatingTo(args: EventData) {
6+
page = <Page>args.object;
7+
page.bindingContext = new SampleData();
8+
}
9+
10+
export class SampleData extends Observable {
11+
textInput = '';
12+
textChange(args) {
13+
console.log(args.object.text);
14+
this.notifyPropertyChange('textInput', args.object.text);
15+
}
16+
}

apps/toolbox/src/pages/forms.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
2+
<Page.actionBar>
3+
<ActionBar title="Labels and TextView" class="action-bar">
4+
</ActionBar>
5+
</Page.actionBar>
6+
<ScrollView>
7+
<StackLayout padding="20">
8+
<Label text="TextField:" fontWeight="bold" />
9+
<TextField textChange="{{ textChange }}" marginTop="6" backgroundColor="#efefef" padding="8" fontSize="18" keyboardType="url" />
10+
11+
<Label text="{{ textInput }}" marginTop="6" />
12+
13+
</StackLayout>
14+
</ScrollView>
15+
</Page>

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"css": "^3.0.0",
4545
"css-tree": "^1.1.2",
4646
"dotenv": "10.0.0",
47+
"emoji-regex": "^10.2.1",
4748
"eslint": "7.22.0",
4849
"eslint-config-prettier": "^8.1.0",
4950
"gonzales": "^1.0.7",

packages/core/index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export type { InstrumentationMode, TimerInfo } from './profiling';
105105
export { encoding } from './text';
106106
export * from './trace';
107107
export * from './ui';
108-
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';
108+
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';
109109
import { SDK_VERSION } from './utils/constants';
110110
import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback, numberHasDecimals, numberIs64Bit } from './utils/types';
111111
export declare const Utils: {
@@ -163,5 +163,6 @@ export declare const Utils: {
163163
dismissSoftInput: typeof dismissSoftInput;
164164
dismissKeyboard: typeof dismissKeyboard;
165165
copyToClipboard: typeof copyToClipboard;
166+
isEmoji: typeof isEmoji;
166167
};
167168
export { XmlParser, ParserEventType, ParserEvent } from './xml';

packages/core/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export * from './trace';
137137

138138
export * from './ui';
139139

140-
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';
140+
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';
141141
import { SDK_VERSION } from './utils/constants';
142142
import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback, numberHasDecimals, numberIs64Bit } from './utils/types';
143143

@@ -199,6 +199,7 @@ export const Utils = {
199199
dismissSoftInput,
200200
dismissKeyboard,
201201
copyToClipboard,
202+
isEmoji,
202203
};
203204

204205
export { XmlParser, ParserEventType, ParserEvent } from './xml';

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"@nativescript/hook": "~2.0.0",
4848
"acorn": "^8.7.0",
4949
"css-tree": "^1.1.2",
50+
"emoji-regex": "^10.2.1",
5051
"reduce-css-calc": "^2.1.7",
5152
"tslib": "^2.0.0"
5253
},

packages/core/ui/text-field/index.ios.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { hintProperty, placeholderColorProperty, _updateCharactersInRangeReplace
44
import { CoreTypes } from '../../core-types';
55
import { Color } from '../../color';
66
import { colorProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty } from '../styling/style-properties';
7-
import { layout } from '../../utils';
7+
import { layout, isEmoji } from '../../utils';
88
import { profile } from '../../profiling';
99

1010
export * from './text-field-common';
@@ -200,7 +200,10 @@ export class TextField extends TextFieldBase {
200200
}
201201

202202
if (this.updateTextTrigger === 'textChanged') {
203-
const shouldReplaceString = (textField.secureTextEntry && this.firstEdit) || delta > 1;
203+
// 1. secureTextEntry with firstEdit should not replace
204+
// 2. emoji's should not replace value
205+
// 3. convenient keyboard shortcuts should not replace value (eg, '.com')
206+
const shouldReplaceString = (textField.secureTextEntry && this.firstEdit) || (delta > 1 && !isEmoji(replacementString) && delta !== replacementString.length);
204207
if (shouldReplaceString) {
205208
textProperty.nativeValueChange(this, replacementString);
206209
} else {

packages/core/utils/common.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as types from './types';
22
import { dispatchToMainThread, dispatchToUIThread, isMainThread } from './mainthread-helper';
33
import { sanitizeModuleName } from '../ui/builder/module-name-sanitizer';
4+
import emojiRegex from 'emoji-regex';
45

56
import { GC } from './index';
67

@@ -203,3 +204,9 @@ export function queueGC(delay = 900, useThrottle?: boolean) {
203204
debouncedGC.get(delay)();
204205
}
205206
}
207+
208+
export function isEmoji(value: string): boolean {
209+
// TODO: In a future runtime update, we can switch to using Unicode Property Escapes:
210+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes
211+
return emojiRegex().test(value);
212+
}

0 commit comments

Comments
 (0)