Code Monkey home page Code Monkey logo

nativescript-keyboard-toolbar's Introduction

NativeScript Keyboard Toolbar

Build Status NPM version Downloads Twitter Follow

iOS and Android running the included demo - much better framerate on YouTube!

What The Keyboard!?

Glad you asked ๐Ÿ˜…!

  • โŒจ๏ธ Mobile keyboards are a compromise at best. Let's make them easier to work with by attaching a toolbar on top of it.
  • ๐Ÿฅ… Design goal = declare any NativeScript layout and stick it on top of the soft keyboard.
  • ๐Ÿ’ Make the toolbar stick to the keyboard, no matter its shape or form.
  • ๐Ÿ™…โ€โ™€๏ธ No third party dependencies; use only stuff from tns-core-modules (which your app already has).
  • โ™พ Allow multiple toolbar designs on one page.

Installation

tns plugin add nativescript-keyboard-toolbar

General usage instructions

The plugin works by grabbing your declared toolbar layout and moving it off-screen.

Then, whenever the related TextField or TextView received focus, the plugin animates the toolbar to the top of the keyboard and keeps it there until the field loses focus.

For this to behave properly you'll need to grab any layout you currently have and wrap it in a GridLayout as show in the examples below. The GridLayout allows for stacking multiple child layout on top of each other when their row and col properties are equal (or omitted).

So if your layout structure is currently this:

<ActionBar/>
<StackLayout/>

Change it to this:

<ActionBar/>
<GridLayout>
    <StackLayout/>
    <Toolbar/>
</GridLayout>

Not too bad, right? That will make the Toolbar stack on top of the StackLayout you already had.

Note that the plugin could have done this for you, or take some other approach entirely, but many hours of tinkering has convinced me this is the best solution.

Usage with NativeScript Core

Mind the comments in the example below.

<Page xmlns="http://schemas.nativescript.org/tns.xsd" xmlns:kt="nativescript-keyboard-toolbar">

    <!-- This GridLayout wrapper is required; it wraps the visible layout and the Toolbar layout(s) -->
    <GridLayout>

        <StackLayout>
            <Label text="Some text"/>
            <!-- Add an 'id' property that we can reference below -->
            <TextField id="priceTextField" hint="Enter the price" keyboardType="number"/>
        </StackLayout>

        <!-- The 'forId' and 'height' properties are mandatory -->
        <kt:Toolbar forId="priceTextField" height="44">
            <GridLayout columns="*, *, *" class="toolbar">
                <Label col="0" text="๐Ÿ‘" tap="{{ appendToTextView }}"/>
                <Label col="1" text="๐Ÿ‘Ž" tap="{{ appendToTextView }}"/>
                <Label col="2" text="๐Ÿ˜„" tap="{{ appendToTextView }}"/>
            </GridLayout>
        </kt:Toolbar>

    </GridLayout>
</Page>

Core demo app

Check the source in the demo folder, or run it on your own device:

git clone https://github.com/EddyVerbruggen/nativescript-keyboard-toolbar
cd nativescript-keyboard-toolbar/src
npm i
npm run demo.ios # or .android

Usage with NativeScript-Angular

Register the plugin in a specific module, or globally in the app module:

import { registerElement } from "nativescript-angular";
registerElement("KeyboardToolbar", () => require("nativescript-keyboard-toolbar").Toolbar);

In this example, we're adding a TextField to the ActionBar. Note that we still need to wrap the rest of the page in a GridLayout:

<ActionBar>
  <TextField #textField1 id="tf1"></TextField>
</ActionBar>

<!-- Our Toolbar wrapper - no need for 'columns' / 'rows' properties because we want the children to stack -->
<GridLayout>

  <!-- Add whatever visible layout you need here -->
  <ListView [items]="items">
    <ng-template let-item="item">
      <Label [nsRouterLink]="['/item', item.id]" [text]="item.name" class="list-group-item"></Label>
    </ng-template>
  </ListView>

  <!-- Use 'KeyboardToolbar' because that's what we registered in our module (see above).
   The 'forId' and 'height' properties are mandatory -->
  <KeyboardToolbar forId="tf1" height="44">
    <GridLayout columns="*, *, *, auto" class="toolbar">
      <Label col="0" text="๐Ÿ‘" (tap)="appendToTextField(textField1, '๐Ÿ‘')"></Label>
      <Label col="1" text="๐Ÿ‘Ž" (tap)="appendToTextField(textField1, '๐Ÿ‘Ž')"></Label>
      <Label col="2" text="๐Ÿ˜„" (tap)="appendToTextField(textField1, '๐Ÿ˜„')"></Label>
      <Label col="3" text="Close๏ธ" (tap)="closeKeyboard(textField1)"></Label>
    </GridLayout>
  </KeyboardToolbar>
</GridLayout>

Angular demo app

Check the source in the demo-ng folder, or run it on your own device:

git clone https://github.com/EddyVerbruggen/nativescript-keyboard-toolbar
cd nativescript-keyboard-toolbar/src
npm i
npm run demo-ng.ios # or .android

Usage with NativeScript-Vue

Register the plugin in app.js (or depending on your app's setup: app.ts, or main.js, etc):

import Vue from "nativescript-vue";
Vue.registerElement('KeyboardToolbar', () => require('nativescript-keyboard-toolbar').Toolbar);
<template>
  <Page class="page">
    <ActionBar class="action-bar">
      <Label class="action-bar-title" text="Home"></Label>
    </ActionBar>

    <!-- Our Toolbar wrapper - no need for 'columns' / 'rows' properties because we want the children to stack -->
    <GridLayout>

      <StackLayout>
        <TextView id="tv2" text="Say it with emoji!"/>
      </StackLayout>

      <!-- Use 'KeyboardToolbar' because that's what we registered in our module (see above).
         The 'forId' and 'height' properties are mandatory -->
      <KeyboardToolbar forId="tv2" height="44">
        <GridLayout columns="*, *, *" class="toolbar">
          <Label col="0" text="๐Ÿ‘" @tap="appendToTextView2"/>
          <Label col="1" text="๐Ÿ‘Ž" @tap="appendToTextView2"/>
          <Label col="2" text="๐Ÿ˜„" @tap="appendToTextView2"/>
        </GridLayout>
      </KeyboardToolbar>

    </GridLayout>
  </Page>
</template>

<script>
  import { topmost } from "tns-core-modules/ui/frame";

  export default {
    methods: {
      appendToTextView2(args) {
        const textView = topmost().currentPage.getViewById("tv2");
        textView.text += args.object.text;
      }
    }
  }
</script>

Vue demo app

Check the source in the demo-vue folder, or run it on your own device:

git clone https://github.com/EddyVerbruggen/nativescript-keyboard-toolbar
cd nativescript-keyboard-toolbar/src
npm i
npm run demo-vue.ios # or .android

What about IQKeyboardManager?

If you have IQKeyboardManager installed for better keyboard behavior on iOS, then this plugin will detect it and add the height of your custom toolbar to the scroll offset IQKeyboardManager applies. You can see this in action in the NativeScript Core demo app.

You may want to suppress IQKeyboardManager's own toolbar in this case (to avoid a double toolbar), as shown here.

Future work

  • Orientation-change support.
  • Dismiss keyboard on iOS when tapping outside the keyboard (configurable). Fot the time being you can add and configure IQKeyboardManager as mentioned above.
  • Auto-scroll the view if the keyboard overlaps the text field (on iOS you can already do that with IQKeyboardManager).
  • Modal support on Android (currently you can't use the toolbar in a modal because the toolbar is behind the modal)

nativescript-keyboard-toolbar's People

Contributors

eddyverbruggen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

nativescript-keyboard-toolbar's Issues

Compliment

@EddyVerbruggen This is your best yet bud, awesome work! I've been wishing for something like this for ages! Can't wait to use it!

Thanks you!!

Support IQKeyboardManager

IQKeyboardManager scrolls editable text into view but isn't aware of the toolbar. So let's add the toolbar height to whatever height IQKeyboardManager uses.

Users can add code to accomplish this in their app, but let's make the plugin a little smarter.

Tabbed Layout forId problem

Make sure to check the demo app(s) for sample usage

Demo app works

If the demo apps cannot help and there is no issue for your problem, tell us.

I see the message on console

Please make sure forId="" resolves to a visible view, or the toolbar won't render correctly! Example:

When it does, the keyboard toolbar does not show no matter how many times I click it the triggering textfield. Then I click the Home button (Android) and click the overview button to go back to the app, then the textfield triggers the keyboardtoolbar and everything works fine!

1 out of 10 times the message does not appear and everything works fine on multiple tabs. Sometimes it only affects some of the keyboardtoolbars.

Which platform(s) does your issue occur on?

  • Android / Not checked iOS

Please, provide the following version numbers that your issue occurs with:

  • CLI: 6.0.2
  • Runtime(s):
    "tns-android": {
    "version": "6.0.0"
    },
    "tns-ios": {
    "version": "6.0.1"
    }
  • Plugin(s):
    "dependencies": {
    "@angular/animations": "8.0.0",
    "@angular/common": "8.0.0",
    "@angular/compiler": "8.0.0",
    "@angular/core": "8.0.0",
    "@angular/forms": "8.0.0",
    "@angular/http": "^8.0.0-beta.10",
    "@angular/platform-browser": "8.0.0",
    "@angular/platform-browser-dynamic": "8.0.0",
    "@angular/router": "8.0.0",
    "@nstudio/nativescript-pulltorefresh": "^1.0.1",
    "@types/node": "^11.13.18",
    "crypto-js": "^3.1.9-1",
    "email-validator": "^2.0.4",
    "moment": "^2.24.0",
    "nativescript-angular": "8.0.2",
    "nativescript-background-http": "^3.4.1",
    "nativescript-barcodescanner": "^3.2.1",
    "nativescript-camera": "^4.5.0",
    "nativescript-carousel": "^6.1.0",
    "nativescript-contacts": "^1.6.2",
    "nativescript-crossplatform-aes": "^1.0.3",
    "nativescript-downloader": "^2.1.4",
    "nativescript-feedback": "^1.3.10",
    "nativescript-filter-select": "^1.3.0",
    "nativescript-imagecropper": "^1.0.6",
    "nativescript-keyboard-toolbar": "^1.0.4",
    "nativescript-loading-indicator": "^2.5.2",
    "nativescript-local-notifications": "^4.0.1",
    "nativescript-localstorage": "^2.0.0",
    "nativescript-modal-datetimepicker": "^1.2.0",
    "nativescript-permissions": "^1.3.6",
    "nativescript-phone": "^1.4.0",
    "nativescript-ripple": "^2.2.1",
    "nativescript-social-share": "^1.5.2",
    "nativescript-theme-core": "~1.0.4",
    "nativescript-ui-chart": "5.0.0",
    "nativescript-ui-dataform": "5.0.0",
    "nativescript-ui-listview": "7.0.0",
    "nativescript-web-image-cache": "^5.0.0",
    "reflect-metadata": "~0.1.12",
    "rxjs": "~6.3.0",
    "tns-core-modules": "~6.0.1",
    "uglifyjs-webpack-plugin": "^2.1.3",
    "webpack-bundle-analyzer": "^3.3.2",
    "xmldom": "^0.1.27",
    "xpath": "0.0.27",
    "zone.js": "0.9.1"
    },
    "devDependencies": {
    "@angular/compiler-cli": "8.0.0",
    "@nativescript/schematics": "~0.5.0",
    "@ngtools/webpack": "8.0.0",
    "node-sass": "4.12.0",
    "nativescript-dev-webpack": "^1.0.1",
    "tns-platform-declarations": "6.0.1",
    "typescript": "3.4.5"
    },

Please, tell us how to recreate the issue in as much detail as possible.

I tried to recreate the issue using an empty tabbed layout and multiple keyboardtoolbars. the issue did not reoccur. I think it might be related to loading times of the tab contents.

Is there any code involved?

I used the sample code, not much of extras:

           <TextField #tfFilterTask id="tf2" row="1" col="0" style="placeholder-color: rgba(255,255,255,0.6)"
                       class="c-white m-l-5" hint="Kod veya isim..." [text]=filterTaskText
                       (textChange)="onFilterChange($event)"></TextField>

    <KeyboardToolbar2 forId="tf2" height="50">
        <GridLayout columns="auto, *,auto,auto, auto" class="c-white p-10" backgroundColor="#26252A">
            <Label col="0" class="fas fs-24" verticalAlignment="center" text="&#xf002; Ara: "></Label>
            <Label col="1" class="fs-24" verticalAlignment="center" [text]=filterTaskText></Label>
            <Label col="2" class="fas c-white p-5 m-l-20" verticalAlignment="center" textWrap="false" text="&#xf069;"></Label>
            <Label col="3" class="fas c-white p-5 m-l-20" verticalAlignment="center" textWrap="false" text="&#xf2ed;" (tap)="clearKeyboard()"></Label>
            <Label col="4" class="fas c-white p-5 m-l-20" verticalAlignment="center" textWrap="false" text="&#xf00d;" (tap)="closeKeyboard(tfFilterTask)"></Label>
        </GridLayout>
    </KeyboardToolbar2>

Thank you for this great plugin, I hope we can solve this issue as I'd really like to use it.

Support for WebView

It would be nice to use such a toolbar also for text inputs within web views. Is this possible in any way?

Toolbar appeared on bottom for android phones with physical navigation bar

If the demo apps cannot help and there is no issue for your problem, tell us about it

First of all, thanks for the great plugin. I have a test on this plugin, and find it works like a charm on the simulator and phones with the virtual navigation bar. But when it runs on the phone with the physical navigation bar, there are 2 problems:

  • The toolbar is showed on the bottom when keyboard is hide
  • The toolbar is showed above keyboard with a gap when keyboard is showed

Which platform(s) does your issue occur on?

Android (phones with physical navigation bar)

Is there any code involved?

I've debugged with the real device and found out that the problem is caused by the code here

  • The first problem is caused by this:
// at this point, topmost().currentPage is null, so do it like this:
    let page: any = parent;
    while (!page && !page.frame) {
      page = page.parent;
    }

    const {y} = parent.getLocationOnScreen();
    const newHeight = parent.getMeasuredHeight();

    // this is the bottom navbar - which may be hidden by the user.. so figure out its actual height
    this.isNavbarVisible = page.getMeasuredHeight() < Toolbar.getUsableScreenSizeY();
    this.navbarHeight = Toolbar.getNavbarHeight();

I find that the page.getMeasuredHeight() is returning the exact height as the newHeight which clearly is wrong.
Further more, I tried using parent.page.getMeasuredHeight() to compare with Toolbar.getUsableScreenSizeY() which is still not working(maybe because getUsableScreenSizeY is taking status bar into account).

Anyway, so I change the way to find out if there is a virtual navigation bar. You can see the complete code below.


  • The second problem is caused by this:
private showToolbar(parent): void {
    const animateToY = this.startPositionY - this.lastKeyboardHeight - (this.showWhenKeyboardHidden === true ? 0 : (this.lastHeight / screen.mainScreen.scale)) - (this.isNavbarVisible ? 0 : this.navbarHeight);

I guess the problem here is that there is actually no need to minus the this.navbarHeight anymore. Since the only thing changed should just be the lastKeyboardHeight, right?


You can see the complete changes here, hope this can be helpful.

PS: I'm also using the getViewForId in android since I've encountered that problem appeared in iOS.

Better NativeScript 6 compatibility

There's a problem with frame.topmost() in N6. It's undefined in the demo app when I bump it to N6, so let's see if we can get rid of it entirely.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.