Skip to content

Commit 75af593

Browse files
committed
feat(react): Tabs, TabStrip, TabStripItem, BottomNavigation, and TabContentItem for NativeScript 8
1 parent 9fca9a0 commit 75af593

File tree

6 files changed

+562
-0
lines changed

6 files changed

+562
-0
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
"ng-packagr": "~11.2.0",
8181
"prettier": "^2.2.1",
8282
"prompt": "^1.1.0",
83+
"react-nativescript": "^3.0.0-beta.1",
8384
"rimraf": "^3.0.2",
8485
"rxjs": "^6.6.6",
8586
"ts-patch": "^1.3.2",

packages/bottom-navigation/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,63 @@ Vue.use(BottomNavigation);
214214
</MDBottomNavigation>
215215
```
216216

217+
##
218+
219+
### NativeScript + React
220+
221+
First, register the component before any of your React NativeScript app renders. A good place to put this code is in your entrypoint file (which may be called `src/app.ts` or similar), before the `ReactNativeScript.start` function is called. Here's how to install it:
222+
223+
```ts
224+
import { registerBottomNavigation } from '@nativescript-community/ui-material-bottom-navigation/react';
225+
226+
registerBottomNavigation();
227+
```
228+
229+
When available (I've not implemented it at the time of writing, but intend to in time), it would be best to use this component via the `bottomNavigationNavigatorFactory()` API exported by [React NativeScript Navigation](https://github.com/shirakaba/react-nativescript-navigation/tree/master/react-nativescript-navigation), but here's how to use it directly:
230+
231+
```tsx
232+
import * as React from "react";
233+
234+
function ExampleTabs(){
235+
return (
236+
<bottomNavigation selectedIndex={1}>
237+
{/* The bottomTab UI is created via tabStrip (the container) and tabStripItem (for each tab) */}
238+
<tabStrip>
239+
<tabStripItem>
240+
<label>Home</label>
241+
<image src="font://&#xf015;" className="fas"/>
242+
</tabStripItem>
243+
<tabStripItem className="special">
244+
<label>Account</label>
245+
<image src="font://&#xf007;" className="fas"/>
246+
</tabStripItem>
247+
<tabStripItem className="special">
248+
<label>Search</label>
249+
<image src="font://&#xf00e;" className="fas"/>
250+
</tabStripItem>
251+
</tabStrip>
252+
253+
{/* The number of tabContentItem components should corespond to the number of TabStripItem components */}
254+
<tabContentItem>
255+
<gridLayout>
256+
<label className="h2 text-center">Home Page</label>
257+
</gridLayout>
258+
</tabContentItem>
259+
<tabContentItem>
260+
<gridLayout>
261+
<label className="h2 text-center">Account Page</label>
262+
</gridLayout>
263+
</tabContentItem>
264+
<tabContentItem>
265+
<gridLayout>
266+
<label className="h2 text-center">Search Page</label>
267+
</gridLayout>
268+
</tabContentItem>
269+
</bottomNavigation>
270+
);
271+
}
272+
```
273+
217274
## API
218275

219276
### Attributes

packages/tabs/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,63 @@ Vue.use(TabsPlugin);
214214
</MDTabs>
215215
```
216216

217+
##
218+
219+
### NativeScript + React
220+
221+
First, register the component before any of your React NativeScript app renders. A good place to put this code is in your entrypoint file (which may be called `src/app.ts` or similar), before the `ReactNativeScript.start` function is called. Here's how to install it:
222+
223+
```ts
224+
import { registerTabs } from '@nativescript-community/ui-material-tabs/react';
225+
226+
registerTabs();
227+
```
228+
229+
Normally it would be best to use this component via the `tabNavigatorFactory()` API exported by [React NativeScript Navigation](https://github.com/shirakaba/react-nativescript-navigation/tree/master/react-nativescript-navigation), but here's how to use it directly:
230+
231+
```tsx
232+
import * as React from "react";
233+
234+
function ExampleTabs(){
235+
return (
236+
<tabs selectedIndex={1}>
237+
{/* The bottomTab UI is created via tabStrip (the container) and tabStripItem (for each tab) */}
238+
<tabStrip>
239+
<tabStripItem>
240+
<label>Home</label>
241+
<image src="font://&#xf015;" className="fas"/>
242+
</tabStripItem>
243+
<tabStripItem className="special">
244+
<label>Account</label>
245+
<image src="font://&#xf007;" className="fas"/>
246+
</tabStripItem>
247+
<tabStripItem className="special">
248+
<label>Search</label>
249+
<image src="font://&#xf00e;" className="fas"/>
250+
</tabStripItem>
251+
</tabStrip>
252+
253+
{/* The number of tabContentItem components should corespond to the number of TabStripItem components */}
254+
<tabContentItem>
255+
<gridLayout>
256+
<label className="h2 text-center">Home Page</label>
257+
</gridLayout>
258+
</tabContentItem>
259+
<tabContentItem>
260+
<gridLayout>
261+
<label className="h2 text-center">Account Page</label>
262+
</gridLayout>
263+
</tabContentItem>
264+
<tabContentItem>
265+
<gridLayout>
266+
<label className="h2 text-center">Search Page</label>
267+
</gridLayout>
268+
</tabContentItem>
269+
</tabs>
270+
);
271+
}
272+
```
273+
217274
## API
218275

219276
### Attributes

src/bottom-navigation/react/index.ts

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { registerElement, NSVElement, NativeScriptProps } from "react-nativescript";
2+
import { warn } from "react-nativescript/dist/shared/logger";
3+
import { BottomNavigation, SelectedIndexChangedEventData, TabContentItem, TabStrip } from '../';
4+
import { TabNavigationBaseAttributes } from "../../core/tab-navigation-base/tab-navigation-base/react";
5+
6+
// Global compile-time constant (for some reason not exported by RNS itself)
7+
declare var __DEV__: boolean;
8+
9+
// ui/bottom-navigation/bottom-navigation.d.ts
10+
export type BottomNavigationAttributes = TabNavigationBaseAttributes & {
11+
android?: any;
12+
ios?: any;
13+
items?: TabContentItem[];
14+
onSelectedIndexChanged?: (args: SelectedIndexChangedEventData) => void;
15+
selectedIndex?: number;
16+
tabStrip?: TabStrip;
17+
};
18+
19+
declare global {
20+
module JSX {
21+
interface IntrinsicElements {
22+
bottomNavigation: NativeScriptProps<BottomNavigationAttributes, BottomNavigation>;
23+
}
24+
interface ElementChildrenAttribute {
25+
children: {};
26+
}
27+
}
28+
}
29+
30+
let installed: boolean = false;
31+
32+
export function registerBottomNavigation(): void {
33+
if(installed){
34+
return;
35+
}
36+
registerElement(
37+
'bottomNavigation',
38+
// @ts-ignore I can assure that this really does extend ViewBase
39+
() => BottomNavigation,
40+
{
41+
// TODO: share the same NodeOps for both BottomNavigation and Tabs; they're identical as they both extend TabNavigationBase.
42+
nodeOps: {
43+
insert(child: NSVElement, parent: NSVElement<BottomNavigation>, atIndex?: number): void {
44+
const bottomNavigation = parent.nativeView;
45+
46+
if(child.nodeRole === "tabStrip"){
47+
if(child.nativeView instanceof TabStrip){
48+
bottomNavigation.tabStrip = child.nativeView;
49+
} else {
50+
if (__DEV__) {
51+
warn(
52+
`Unable to add child "${child.nativeView.constructor.name}" as the tabStrip of <bottomNavigation> as it is not an instance of TabStrip.`
53+
);
54+
}
55+
}
56+
} else if(child.nodeRole === "items"){
57+
if(child.nativeView instanceof TabContentItem === false){
58+
if (__DEV__) {
59+
warn(
60+
`Unable to add child "${child.nativeView.constructor.name}" to the items of <bottomNavigation> as it is not an instance of TabContentItem.`
61+
);
62+
};
63+
return;
64+
}
65+
66+
const items = bottomNavigation.items || []; // Annoyingly, it's the consumer's responsibility to ensure there's an array there!
67+
68+
if(typeof atIndex === "undefined" || atIndex === items.length){
69+
bottomNavigation.items = items.concat(child.nativeView as TabContentItem);
70+
} else {
71+
bottomNavigation.items = items.slice().splice(
72+
atIndex,
73+
0,
74+
child.nativeView as TabContentItem
75+
);
76+
}
77+
} else if(child.nodeRole === "item"){
78+
if (__DEV__) {
79+
warn(
80+
`Unable to add child "${child.nativeView.constructor.name}" to <bottomNavigation> as it had the nodeRole "item"; please correct it to "items".`
81+
);
82+
}
83+
} else {
84+
if (__DEV__) {
85+
warn(
86+
`Unable to add child "${child.nativeView.constructor.name}" to <bottomNavigation> as it does not have a nodeRole specified; ` +
87+
`please set a nodeRole of "tabStrip", or "items".`
88+
)
89+
}
90+
}
91+
},
92+
remove(child: NSVElement, parent: NSVElement<BottomNavigation>): void {
93+
const tabs = parent.nativeView;
94+
95+
if(child.nodeRole === "tabStrip"){
96+
tabs.tabStrip = null; // Anything falsy should work.
97+
} else if(child.nodeRole === "items"){
98+
tabs.items = (tabs.items || []).filter(i => i !== child.nativeView);
99+
} else if(child.nodeRole === "item"){
100+
if (__DEV__) {
101+
warn(
102+
`Unable to remove child "${child.nativeView.constructor.name}" from <bottomNavigation> as it had the nodeRole "item"; please correct it to "items".`
103+
);
104+
}
105+
} else {
106+
if (__DEV__) {
107+
warn(
108+
`Unable to remove child "${child.nativeView.constructor.name}" from <bottomNavigation> as it does not have a nodeRole specified; ` +
109+
`please set a nodeRole of "tabStrip", or "items"`
110+
)
111+
}
112+
}
113+
}
114+
}
115+
}
116+
);
117+
118+
installed = true;
119+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { PropertyChangeData } from '@nativescript/core';
2+
import { ViewAttributes } from 'react-nativescript';
3+
import { SelectedIndexChangedEventData } from '..';
4+
import { TabContentItem } from '../../tab-content-item';
5+
import { TabStrip } from '../../tab-strip';
6+
7+
// ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.d.ts
8+
export type TabNavigationBaseAttributes = ViewAttributes & {
9+
android?: any;
10+
ios?: any;
11+
items?: string | TabContentItem[];
12+
onItemsChange?: (args: PropertyChangeData) => void;
13+
onSelectedIndexChange?: (args: PropertyChangeData) => void;
14+
onSelectedIndexChanged?: (args: SelectedIndexChangedEventData) => void;
15+
onTabStripChange?: (args: PropertyChangeData) => void;
16+
selectedIndex?: string | number;
17+
tabStrip?: string | TabStrip;
18+
};

0 commit comments

Comments
 (0)