Skip to content

Commit c10f72b

Browse files
committed
fix(keyboard): big keyboard/input refactor
fixes #9699 fixes #11484 fixes #11389 fixes #11325 fixes #11291 fixes #10828 fixes #11291 fixes #10393 fixes #10257 fixes #9434 fixes #8933 fixes #7178 fixes #7047 fixes #10552 fixes #10393 fixes #10183 fixes #10187 fixes #10852 fixes #11578
1 parent 47e3c70 commit c10f72b

File tree

27 files changed

+847
-957
lines changed

27 files changed

+847
-957
lines changed

scripts/gulp/tasks/demos.dev.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ task('demos.watch', ['demos.prepare'], (done: Function) => {
1111
done(new Error(`Usage: gulp e2e.watch --folder modal`));
1212
}
1313

14-
serveDemo(folderInfo.componentName).then(() => {
14+
serveDemo(folderInfo.componentName, folderInfo.devApp).then(() => {
1515
done();
1616
}).catch((err: Error) => {
1717
done(err);
1818
});
1919
});
2020

21-
function serveDemo(folderName: any) {
21+
function serveDemo(folderName: any, devApp: boolean) {
2222

2323
const ionicAngularDir = join(PROJECT_ROOT, 'src');
2424
const srcTestRoot = join(DEMOS_ROOT, 'src', folderName);
@@ -40,5 +40,5 @@ function serveDemo(folderName: any) {
4040
const appNgModulePath = join(srcTestRoot, 'app', 'app.module.ts');
4141
const distDir = join(distDemoRoot, 'www');
4242

43-
return runAppScriptsServe(folderName, appEntryPoint, appNgModulePath, ionicAngularDir, distDir, pathToWriteFile, ionicAngularDir, sassConfigPath, copyConfigPath, watchConfigPath);
43+
return runAppScriptsServe(folderName, appEntryPoint, appNgModulePath, ionicAngularDir, distDir, pathToWriteFile, ionicAngularDir, sassConfigPath, copyConfigPath, watchConfigPath, devApp);
4444
}

scripts/gulp/tasks/e2e.dev.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ task('e2e.watch', ['e2e.prepare'], (done: Function) => {
1313
return;
1414
}
1515

16-
serveTest(folderInfo).then(() => {
16+
serveTest(folderInfo, folderInfo.devApp).then(() => {
1717
done();
1818
}).catch((err: Error) => {
1919
done(err);
2020
});
2121
});
2222

23-
function serveTest(folderInfo: any) {
23+
function serveTest(folderInfo: any, devApp: boolean) {
2424

2525
const ionicAngularDir = join(PROJECT_ROOT, 'src');
2626
const srcTestRoot = join(PROJECT_ROOT, 'src', 'components', folderInfo.componentName, 'test', folderInfo.componentTest);
@@ -47,5 +47,5 @@ function serveTest(folderInfo: any) {
4747
const appNgModulePath = join(dirname(appEntryPoint), 'app.module.ts');
4848
const distDir = join(distTestRoot, 'www');
4949

50-
return runAppScriptsServe(join(folderInfo.componentName, folderInfo.componentTest), appEntryPoint, appNgModulePath, ionicAngularDir, distDir, pathToWriteFile, ionicAngularDir, sassConfigPath, copyConfigPath, null);
50+
return runAppScriptsServe(join(folderInfo.componentName, folderInfo.componentTest), appEntryPoint, appNgModulePath, ionicAngularDir, distDir, pathToWriteFile, ionicAngularDir, sassConfigPath, copyConfigPath, null, devApp);
5151
}

scripts/gulp/util.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ export function runWebpack(pathToWebpackConfig: string, done: Function) {
190190
});
191191
}
192192

193-
export function runAppScriptsServe(testOrDemoName: string, appEntryPoint: string, appNgModulePath: string, srcDir: string, distDir: string, tsConfig: string, ionicAngularDir: string, sassConfigPath: string, copyConfigPath: string, watchConfigPath: string) {
193+
export function runAppScriptsServe(testOrDemoName: string, appEntryPoint: string, appNgModulePath: string, srcDir: string, distDir: string, tsConfig: string, ionicAngularDir: string, sassConfigPath: string, copyConfigPath: string, watchConfigPath: string, devApp: boolean) {
194194
console.log('Running ionic-app-scripts serve with', testOrDemoName);
195195
const deepLinksDir = dirname(dirname(appNgModulePath));
196196
let scriptArgs = [
@@ -207,6 +207,9 @@ export function runAppScriptsServe(testOrDemoName: string, appEntryPoint: string
207207
'--copy', copyConfigPath,
208208
'--enableLint', 'false'
209209
];
210+
if (devApp) {
211+
scriptArgs.push('--bonjour');
212+
}
210213

211214
if (watchConfigPath) {
212215
scriptArgs.push('--watch');
@@ -349,9 +352,11 @@ export function getFolderInfo() {
349352
componentName = folderSplit[0];
350353
componentTest = (folderSplit.length > 1 ? folderSplit[1] : 'basic');
351354
}
355+
const devApp = argv.devapp !== undefined;
352356
return {
353357
componentName: componentName,
354-
componentTest: componentTest
358+
componentTest: componentTest,
359+
devApp: devApp
355360
};
356361
}
357362

src/components/alert/alert-component.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export class AlertCmp {
8484
msgId: string;
8585
subHdrId: string;
8686
mode: string;
87+
keyboardResizes: boolean;
8788
gestureBlocker: BlockerDelegate;
8889

8990
constructor(
@@ -99,6 +100,7 @@ export class AlertCmp {
99100
this.gestureBlocker = gestureCtrl.createBlocker(BLOCK_ALL);
100101
this.d = params.data;
101102
this.mode = this.d.mode || config.get('mode');
103+
this.keyboardResizes = config.getBoolean('keyboardResizes', false);
102104
_renderer.setElementClass(_elementRef.nativeElement, `alert-${this.mode}`, true);
103105

104106
if (this.d.cssClass) {
@@ -178,7 +180,7 @@ export class AlertCmp {
178180
}
179181

180182
const hasTextInput = (this.d.inputs.length && this.d.inputs.some(i => !(NON_TEXT_INPUT_REGEX.test(i.type))));
181-
if (hasTextInput && this._plt.is('mobile')) {
183+
if (!this.keyboardResizes && hasTextInput && this._plt.is('mobile')) {
182184
// this alert has a text input and it's on a mobile device so we should align
183185
// the alert up high because we need to leave space for the virtual keboard
184186
// this also helps prevent the layout getting all messed up from

src/components/app/app.ts

+62
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export class App {
3030
private _titleSrv: Title = new Title(DOCUMENT);
3131
private _rootNav: NavController = null;
3232
private _disableScrollAssist: boolean;
33+
private _didScroll = false;
3334

3435
/**
3536
* @hidden
@@ -87,6 +88,11 @@ export class App {
8788
_plt.registerBackButtonAction(this.goBack.bind(this));
8889
this._disableScrollAssist = _config.getBoolean('disableScrollAssist', false);
8990

91+
const blurring = _config.getBoolean('inputBlurring', false);
92+
if (blurring) {
93+
this._enableInputBlurring();
94+
}
95+
9096
runInDev(() => {
9197
// During developement, navPop can be triggered by calling
9298
const win = <any>_plt.win();
@@ -179,6 +185,7 @@ export class App {
179185
*/
180186
setScrolling() {
181187
this._scrollTime = Date.now() + ACTIVE_SCROLLING_TIME;
188+
this._didScroll = true;
182189
}
183190

184191
/**
@@ -289,6 +296,60 @@ export class App {
289296
return recursivePop(this.getActiveNav());
290297
}
291298

299+
/**
300+
* @hidden
301+
*/
302+
_enableInputBlurring() {
303+
console.debug('App: _enableInputBlurring');
304+
let focused = true;
305+
const self = this;
306+
const platform = this._plt;
307+
308+
platform.registerListener(platform.doc(), 'focusin', onFocusin, { capture: true, zone: false, passive: true });
309+
platform.registerListener(platform.doc(), 'touchend', onTouchend, { capture: false, zone: false, passive: true });
310+
311+
function onFocusin(ev: any) {
312+
focused = true;
313+
}
314+
function onTouchend(ev: any) {
315+
// if app did scroll return early
316+
if (self._didScroll) {
317+
self._didScroll = false;
318+
return;
319+
}
320+
const active = <HTMLElement> self._plt.getActiveElement();
321+
if (!active) {
322+
return;
323+
}
324+
// only blur if the active element is a text-input or a textarea
325+
if (SKIP_BLURRING.indexOf(active.tagName) === -1) {
326+
return;
327+
}
328+
329+
// if the selected target is the active element, do not blur
330+
const tapped = ev.target;
331+
if (tapped === active) {
332+
return;
333+
}
334+
if (SKIP_BLURRING.indexOf(tapped.tagName) >= 0) {
335+
return;
336+
}
337+
338+
// skip if div is a cover
339+
if (tapped.classList.contains('input-cover')) {
340+
return;
341+
}
342+
343+
focused = false;
344+
// TODO: find a better way, why 50ms?
345+
platform.timeout(() => {
346+
if (!focused) {
347+
active.blur();
348+
}
349+
}, 50);
350+
}
351+
}
352+
292353
}
293354

294355
function recursivePop(nav: any): Promise<any> {
@@ -322,5 +383,6 @@ function findTopNav(nav: NavController) {
322383
return nav;
323384
}
324385

386+
const SKIP_BLURRING = ['INPUT', 'TEXTAREA', 'ION-INPUT', 'ION-TEXTAREA'];
325387
const ACTIVE_SCROLLING_TIME = 100;
326388
const CLICK_BLOCK_BUFFER_IN_MILLIS = 64;

src/components/checkbox/checkbox.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,6 @@ export class Checkbox extends BaseInput<boolean> implements IonicTapInput, OnDes
118118
super(config, elementRef, renderer, 'checkbox', false, form, item, null);
119119
}
120120

121-
/**
122-
* @hidden
123-
*/
124-
initFocus() {
125-
this._elementRef.nativeElement.querySelector('button').focus();
126-
}
127-
128121
/**
129122
* @hidden
130123
*/
@@ -145,8 +138,8 @@ export class Checkbox extends BaseInput<boolean> implements IonicTapInput, OnDes
145138
/**
146139
* @hidden
147140
*/
148-
_inputCheckHasValue(val: boolean) {
149-
this._item && this._item.setElementClass('item-checkbox-checked', val);
141+
_inputUpdated() {
142+
this._item && this._item.setElementClass('item-checkbox-checked', this._value);
150143
}
151144

152145
}

src/components/datetime/datetime.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ export class DateTime extends BaseInput<DateTimeData> implements AfterContentIni
448448
* @hidden
449449
*/
450450
_inputUpdated() {
451+
super._inputUpdated();
451452
this.updateText();
452453
}
453454

@@ -475,10 +476,6 @@ export class DateTime extends BaseInput<DateTimeData> implements AfterContentIni
475476

476477
@HostListener('click', ['$event'])
477478
_click(ev: UIEvent) {
478-
// do not continue if the click event came from a form submit
479-
if (ev.detail === 0) {
480-
return;
481-
}
482479
ev.preventDefault();
483480
ev.stopPropagation();
484481
this.open();

src/components/input/input.scss

+2-21
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ input.text-input:-webkit-autofill {
9393

9494
width: 100%;
9595
height: 100%;
96+
97+
touch-action: manipulation;
9698
}
9799

98100
.input[disabled] .input-cover {
@@ -127,27 +129,6 @@ input.text-input:-webkit-autofill {
127129
}
128130

129131

130-
// Scroll Assist Input
131-
// --------------------------------------------------
132-
// This input is used to help the app handle
133-
// Next and Previous input tabbing
134-
135-
[next-input] {
136-
@include padding(0);
137-
138-
position: absolute;
139-
bottom: 20px;
140-
141-
width: 1px;
142-
height: 1px;
143-
144-
border: 0;
145-
background: transparent;
146-
147-
pointer-events: none;
148-
}
149-
150-
151132
// Clear Input Icon
152133
// --------------------------------------------------
153134

0 commit comments

Comments
 (0)