Skip to content

Commit

Permalink
v3.5.0 #160
Browse files Browse the repository at this point in the history
  • Loading branch information
iRoachie authored Oct 4, 2019
2 parents 1286098 + c20cf5f commit 6d35aa4
Show file tree
Hide file tree
Showing 8 changed files with 391 additions and 9 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<h2 align="center">React Native Search Bar</h2>

<p align="center">The high-quality
<a href="https://developer.apple.com/documentation/uikit/uisearchbar">native iOS search bar</a> for <a href="https://facebook.github.io/react-native/">react native.</a>
<a href="https://developer.apple.com/documentation/uikit/uisearchbar">native search bar</a> for <a href="https://facebook.github.io/react-native/">react native.</a>

<p align="center">
<a href="https://www.npmjs.com/package/react-native-search-bar">
Expand Down Expand Up @@ -58,7 +58,7 @@ If linking fails, follow the
## Usage

```javascript
import SearchBar from 'react-native-search-bar'
import SearchBar from 'react-native-search-bar';
```

```JSX
Expand All @@ -73,24 +73,24 @@ import SearchBar from 'react-native-search-bar'

### Managing the keyboard

* Show - `this.refs.searchBar.focus();`
* Hide
* `this.refs.searchBar.blur();` - uses the iOS `endEditing:true` method on the
- Show - `this.refs.searchBar.focus();`
- Hide
- `this.refs.searchBar.blur();` - uses the iOS `endEditing:true` method on the
underlying `UISearchBar` view.
* `this.refs.searchBar.unFocus();` - calls `resignFirstResponder` on the
- `this.refs.searchBar.unFocus();` - calls `resignFirstResponder` on the
`UITextField` used by the `UISearchBar`.

### Examples

* Show the keyboard when the view loads:
- Show the keyboard when the view loads:

```javascript
componentDidMount() {
this.refs.searchBar.focus();
}
```

* Hide the keyboard when the user searches:
- Hide the keyboard when the user searches:

```javascript
...
Expand Down
18 changes: 18 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apply plugin: 'com.android.library'

def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

android {
compileSdkVersion safeExtGet('compileSdkVersion', 28)

compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
}

dependencies {
implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
}
2 changes: 2 additions & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.umhan35">
</manifest>
8 changes: 8 additions & 0 deletions android/src/main/java/org/umhan35/Command.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.umhan35;

/**
* Lambda definition.
*/
public interface Command<T> {
void execute(T t);
}
250 changes: 250 additions & 0 deletions android/src/main/java/org/umhan35/RNSearchBarManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
package org.umhan35;

import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.text.InputType;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.SearchView;
import android.widget.TextView;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.RCTEventEmitter;

import java.util.Map;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class RNSearchBarManager extends SimpleViewManager<SearchView> {
@Nonnull
@Override
public String getName() {
return "RNSearchBar";
}

@Override
public @Nullable
Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
MapBuilder.Builder<String, Object> builder = MapBuilder.builder();
registerEvent(builder, "topBlur", "onBlur");
registerEvent(builder, "topSearchButtonPress", "onSearchButtonPress");
registerEvent(builder, "topFocus", "onFocus");

return builder.build();
}

@ReactProp(name = "autoCapitalize")
public void setAutoCapitalize(SearchView searchView, String type) {
int flags = searchView.getInputType();

// Remove capitalization flags.
flags &= ~(InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
| InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
| InputType.TYPE_TEXT_FLAG_CAP_WORDS
);

switch (type) {
case "characters":
flags |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
break;

case "sentences":
flags |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
break;

case "words":
flags |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
break;

default:
break;
}

searchView.setInputType(flags);
}

@ReactProp(name = "editable")
public void setEditable(SearchView searchView, boolean editable) {
processViewRecursive(searchView, view -> view.setEnabled(editable));
}

@ReactProp(name = "keyboardType")
public void setKeyboardType(SearchView searchView, String type) {
int flags = searchView.getInputType();

switch (type) {
case "email-address":
flags = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
break;

case "numeric":
flags = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL;
break;

case "phone-pad":
flags = InputType.TYPE_CLASS_PHONE;
break;

default:
// Remove class and variants.
flags &= ~(InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION);

flags |= InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
break;
}

searchView.setInputType(flags);
}

@ReactProp(name = "placeholder")
public void setPlaceholder(SearchView searchView, @Nullable String placeholder) {
searchView.setQueryHint(placeholder);
}

@ReactProp(name = "returnKeyType")
public void setReturnKeyType(SearchView searchView, String type) {
int flags = searchView.getImeOptions();

// Remove action.
flags &= ~EditorInfo.IME_MASK_ACTION;

switch (type) {
case "done":
flags |= EditorInfo.IME_ACTION_DONE;
break;

case "go":
flags |= EditorInfo.IME_ACTION_GO;
break;

case "next":
flags |= EditorInfo.IME_ACTION_NEXT;
break;

case "send":
flags |= EditorInfo.IME_ACTION_SEND;
break;

default:
flags |= EditorInfo.IME_ACTION_SEARCH;
break;
}

searchView.setImeOptions(flags);
}

@ReactProp(name = "text")
public void setText(SearchView searchView, String text) {
searchView.setQuery(text, false);
}

@ReactProp(name = "textColor", customType = "Color")
public void setTextColor(SearchView searchView, @Nullable Integer color) {
ColorStateList colorList;
if (color == null) {
final int[] attrs = {android.R.attr.textColorPrimary};
final TypedArray a = searchView.getContext().obtainStyledAttributes(0, attrs);
colorList = a.getColorStateList(0);
a.recycle();
} else {
colorList = ColorStateList.valueOf(color);
}

processViewRecursive(searchView, view -> {
if (view instanceof TextView) {
((TextView) view).setTextColor(colorList);
}
});
}

@ReactProp(name = "textFieldBackgroundColor", customType = "Color")
public void setTextFieldBackgroundColor(SearchView searchView, @Nullable Integer color) {
if (color == null) {
searchView.setBackgroundColor(Color.WHITE);
} else {
searchView.setBackgroundColor(color);
}
}

@Nonnull
@Override
protected SearchView createViewInstance(@Nonnull ThemedReactContext reactContext) {
SearchView searchView = new SearchView(reactContext);
searchView.setIconifiedByDefault(false);
searchView.setPaddingRelative(0, 5, 10, 5);

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
WritableMap event = Arguments.createMap();
event.putString("searchText", query);
ReactContext context = (ReactContext) searchView.getContext();
context.getJSModule(RCTEventEmitter.class).receiveEvent(
searchView.getId(),
"topSearchButtonPress",
event
);
return false;
}

@Override
public boolean onQueryTextChange(String newText) {
WritableMap event = Arguments.createMap();
event.putString("text", newText);
ReactContext context = (ReactContext) searchView.getContext();
context.getJSModule(RCTEventEmitter.class).receiveEvent(
searchView.getId(),
"topChange",
event
);
return false;
}
});

searchView.setOnQueryTextFocusChangeListener((view, hasFocus) -> {
ReactContext context = (ReactContext) searchView.getContext();
context.getJSModule(RCTEventEmitter.class).receiveEvent(
searchView.getId(),
hasFocus ? "topFocus" : "topBlur",
null
);
});

return searchView;
}

/**
* Execute a command on the view and all its children.
*/
private void processViewRecursive(View view, Command<View> command) {
command.execute(view);
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
int count = group.getChildCount();
for (int i = 0; i < count; ++i) {
processViewRecursive(group.getChildAt(i), command);
}
}
}

/**
* Helper to register React Native events.
*/
private void registerEvent(MapBuilder.Builder<String, Object> builder, String topLevel, String callback) {
builder.put(
topLevel,
MapBuilder.of(
"phasedRegistrationNames",
MapBuilder.of("bubbled", callback))
);
}
}
Loading

0 comments on commit 6d35aa4

Please sign in to comment.