Skip to content

Commit edda252

Browse files
committed
Allow the tab navigation base components to be registered separately
1 parent 33acdb9 commit edda252

File tree

7 files changed

+231
-195
lines changed

7 files changed

+231
-195
lines changed

demo-react/src/app.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ purpose of the file is to pass control to the app’s first module.
1010
*/
1111

1212
import * as ReactNativeScript from 'react-nativescript';
13+
import { registerTabNavigationBase } from '@nativescript-community/ui-material-core/tab-navigation-base/react';
1314
import { registerTabs } from '@nativescript-community/ui-material-tabs/react';
1415
import { registerBottomNavigation } from '@nativescript-community/ui-material-bottom-navigation/react';
1516
import { AppContainer } from './components/AppContainer';
1617

18+
registerTabNavigationBase({ enableDebugLogging: true });
1719
registerTabs({ enableDebugLogging: true });
1820
registerBottomNavigation({ enableDebugLogging: true });
1921
ReactNativeScript.start(React.createElement(AppContainer, {}, null));

packages/bottom-navigation/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,10 @@ Vue.use(BottomNavigation);
221221
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:
222222

223223
```ts
224+
import { registerTabNavigationBase } from '@nativescript-community/ui-material-core/tab-navigation-base/react';
224225
import { registerBottomNavigation } from '@nativescript-community/ui-material-bottom-navigation/react';
225226

227+
registerTabNavigationBase();
226228
registerBottomNavigation();
227229
```
228230

@@ -290,7 +292,7 @@ Make sure that you have:
290292
1. Installed the `react-nativescript` npm module.
291293
2. Installed the `@types/react` npm module, strictly with the exact version provided in the [React NativeScript starter template](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-blank-react).
292294
3. Run the postinstall script that comes with the React NativeScript template – `npm run postinstall` – to patch `@types/react`.
293-
4. Registered the component as described above (i.e. import and run the `registerBottomNavigation()` method).
295+
4. Registered the component as described above (i.e. import and run the `registerTabNavigationBase()` and `registerBottomNavigation()` methods).
294296
5. If using Visual Studio Code, option-click the import `@nativescript-community/ui-material-bottom-navigation/react` to jump to the file; opening the file will force it to load its provided typings.
295297

296298
## API

packages/tabs/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,10 @@ Vue.use(TabsPlugin);
221221
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:
222222

223223
```ts
224+
import { registerTabNavigationBase } from '@nativescript-community/ui-material-core/tab-navigation-base/react';
224225
import { registerTabs } from '@nativescript-community/ui-material-tabs/react';
225226

227+
registerTabNavigationBase();
226228
registerTabs();
227229
```
228230

@@ -290,7 +292,7 @@ Make sure that you have:
290292
1. Installed the `react-nativescript` npm module.
291293
2. Installed the `@types/react` npm module, strictly with the exact version provided in the [React NativeScript starter template](https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-blank-react).
292294
3. Run the postinstall script that comes with the React NativeScript template – `npm run postinstall` – to patch `@types/react`.
293-
4. Registered the component as described above (i.e. import and run the `registerTabs()` method).
295+
4. Registered the component as described above (i.e. import and run the `registerTabNavigationBase()` and `registerTabs()` methods).
294296
5. If using Visual Studio Code, option-click the import `@nativescript-community/ui-material-tabs/react` to jump to the file; opening the file will force it to load its provided typings.
295297

296298
## API

src/bottom-navigation/react/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { NSVElement, NativeScriptProps, registerElement } from 'react-nativescript';
22
import { warn } from 'react-nativescript/dist/shared/Logger';
33
import { BottomNavigation, SelectedIndexChangedEventData, TabContentItem, TabStrip } from '../';
4-
import { TabNavigationBaseAttributes } from '@nativescript-community/ui-material-core/tab-navigation-base/tab-navigation-base/react';
4+
import { TabNavigationBaseAttributes } from '@nativescript-community/ui-material-core/tab-navigation-base/react';
55

66
// ui/bottom-navigation/bottom-navigation.d.ts
77
export type BottomNavigationAttributes = TabNavigationBaseAttributes & {
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
import { ContentViewAttributes, NSVElement, NSVViewFlags, NativeScriptProps, ViewAttributes, registerElement } from 'react-nativescript';
2+
import { Color, EventData, Image, Label, PropertyChangeData } from '@nativescript/core';
3+
import { warn } from 'react-nativescript/dist/shared/Logger';
4+
import { SelectedIndexChangedEventData } from '../tab-navigation-base';
5+
import { TabContentItem } from '../tab-content-item';
6+
import { TabStrip } from '../tab-strip';
7+
import { TabStripItem } from '../tab-strip-item';
8+
9+
// ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.d.ts
10+
export type TabNavigationBaseAttributes = ViewAttributes & {
11+
android?: any;
12+
ios?: any;
13+
items?: string | TabContentItem[];
14+
onItemsChange?: (args: PropertyChangeData) => void;
15+
onSelectedIndexChange?: (args: PropertyChangeData) => void;
16+
onSelectedIndexChanged?: (args: SelectedIndexChangedEventData) => void;
17+
onTabStripChange?: (args: PropertyChangeData) => void;
18+
selectedIndex?: string | number;
19+
tabStrip?: string | TabStrip;
20+
};
21+
22+
// ui/tab-navigation-base/tab-content-item/tab-content-item.d.ts
23+
export type TabContentItemAttributes = ContentViewAttributes & {
24+
canBeLoaded?: false | true;
25+
};
26+
27+
// ui/tab-navigation-base/tab-strip/tab-strip.d.ts
28+
export type TabStripAttributes = ViewAttributes & {
29+
highlightColor?: string | Color;
30+
iosIconRenderingMode?: 'automatic' | 'alwaysOriginal' | 'alwaysTemplate';
31+
isIconSizeFixed?: string | false | true;
32+
items?: string | TabStripItem[];
33+
onHighlightColorChange?: (args: PropertyChangeData) => void;
34+
onIosIconRenderingModeChange?: (args: PropertyChangeData) => void;
35+
onIsIconSizeFixedChange?: (args: PropertyChangeData) => void;
36+
onItemTap?: (args: EventData) => void;
37+
onItemsChange?: (args: PropertyChangeData) => void;
38+
onSelectedItemColorChange?: (args: PropertyChangeData) => void;
39+
onUnSelectedItemColorChange?: (args: PropertyChangeData) => void;
40+
selectedItemColor?: string | Color;
41+
unSelectedItemColor?: string | Color;
42+
};
43+
44+
// ui/tab-navigation-base/tab-strip-item/tab-strip-item.d.ts
45+
export type TabStripItemAttributes = ViewAttributes & {
46+
iconClass?: string;
47+
iconSource?: string;
48+
image?: Image;
49+
label?: Label;
50+
onTap?: (args: EventData) => void;
51+
title?: string;
52+
};
53+
54+
declare global {
55+
// eslint-disable-next-line @typescript-eslint/prefer-namespace-keyword
56+
module JSX {
57+
interface IntrinsicElements {
58+
tabStrip: NativeScriptProps<TabStripAttributes, TabStrip>;
59+
tabStripItem: NativeScriptProps<TabStripItemAttributes, TabStripItem>;
60+
tabContentItem: NativeScriptProps<TabContentItemAttributes, TabContentItem>;
61+
}
62+
interface ElementChildrenAttribute {
63+
children: {};
64+
}
65+
}
66+
}
67+
let installed: boolean = false;
68+
69+
interface RegisterTabsOptions {
70+
enableDebugLogging?: boolean;
71+
}
72+
73+
export function registerTabNavigationBase(opts: RegisterTabsOptions = {}): void {
74+
const { enableDebugLogging = false } = opts;
75+
76+
if (installed) {
77+
return;
78+
}
79+
80+
registerElement(
81+
'tabStrip',
82+
// @ts-ignore I can assure that this really does extend ViewBase
83+
() => TabStrip,
84+
{
85+
nodeOps: {
86+
insert(child: NSVElement, parent: NSVElement<TabStrip>, atIndex?: number): void {
87+
const tabStrip = parent.nativeView;
88+
89+
if (child.nodeRole === 'items') {
90+
if (child.nativeView instanceof TabStripItem === false) {
91+
if (enableDebugLogging) {
92+
warn(`Unable to add child "${child.nativeView.constructor.name}" to the items of <tabStrip> as it is not an instance of TabStripItem.`);
93+
}
94+
return;
95+
}
96+
97+
// console.log(`[tabStrip.insert] 1 [${parent} > ${child} @${atIndex}] => [${parent.childNodes}]`);
98+
99+
const items = tabStrip.items || []; // Annoyingly, it's the consumer's responsibility to ensure there's an array there!
100+
if (typeof atIndex === 'undefined' || atIndex === items.length) {
101+
// console.log(`[tabStrip.insert] 2a [${parent} > ${child} @${atIndex}] => [${parent.childNodes}]`);
102+
tabStrip._addChildFromBuilder('items', child.nativeView as TabStripItem);
103+
} else {
104+
// console.log(`[tabStrip.insert] 2b [${parent} > ${child} @${atIndex}] => [${parent.childNodes}]`);
105+
const itemsClone = items.slice();
106+
itemsClone.splice(atIndex, 0, child.nativeView as TabStripItem);
107+
tabStrip.items = itemsClone;
108+
}
109+
} else if (child.nodeRole === 'item') {
110+
if (enableDebugLogging) {
111+
warn(`Unable to add child "${child.nativeView.constructor.name}" to <tabStrip> as it had the nodeRole "item"; please correct it to "items".`);
112+
}
113+
} else {
114+
if (enableDebugLogging) {
115+
warn(
116+
`Unable to add child "${child.nativeView.constructor.name}" to <tabStrip> as it does not have a nodeRole specified; ` +
117+
'please set a nodeRole of "tabStrip", or "items".'
118+
);
119+
}
120+
}
121+
},
122+
remove(child: NSVElement, parent: NSVElement<TabStrip>): void {
123+
const tabStrip = parent.nativeView;
124+
125+
if (child.nodeRole === 'items') {
126+
tabStrip.items = (tabStrip.items || []).filter((i) => i !== child.nativeView);
127+
} else if (child.nodeRole === 'item') {
128+
if (enableDebugLogging) {
129+
warn(`Unable to remove child "${child.nativeView.constructor.name}" from <tabStrip> as it had the nodeRole "item"; please correct it to "items".`);
130+
}
131+
} else {
132+
if (enableDebugLogging) {
133+
warn(
134+
`Unable to remove child "${child.nativeView.constructor.name}" from <tabStrip> as it does not have a nodeRole specified; ` +
135+
'please set a nodeRole of "tabStrip", or "items"'
136+
);
137+
}
138+
}
139+
}
140+
}
141+
}
142+
);
143+
144+
registerElement(
145+
'tabStripItem',
146+
// @ts-ignore I can assure that this really does extend ViewBase
147+
() => TabStripItem,
148+
{
149+
nodeOps: {
150+
insert(child: NSVElement, parent: NSVElement<TabStripItem>, atIndex?: number): void {
151+
const tabStripItem = parent.nativeView;
152+
153+
// Note: The instanceof check, and nodeRole check, is technically redundant if you look at the implementation, but I'll
154+
// keep these good practices in case it's ever refactored.
155+
156+
if (child.nodeRole === 'label') {
157+
console.log(`[tabStripItem.insert] LABEL [${parent} > ${child} @${atIndex}] => [${parent.childNodes}]`);
158+
if (child.nativeView instanceof Label === false) {
159+
if (enableDebugLogging) {
160+
warn(`Unable to add child "${child.nativeView.constructor.name}" to the items of <tabStripItem> as it is not an instance of Label.`);
161+
}
162+
return;
163+
}
164+
165+
tabStripItem.label = child.nativeView as Label;
166+
} else if (child.nodeRole === 'image') {
167+
console.log(`[tabStripItem.insert] IMAGE [${parent} > ${child} @${atIndex}] => [${parent.childNodes}]`);
168+
if (child.nativeView instanceof Image === false) {
169+
if (enableDebugLogging) {
170+
warn(`Unable to add child "${child.nativeView.constructor.name}" to the items of <tabStripItem> as it is not an instance of Image.`);
171+
}
172+
return;
173+
}
174+
175+
tabStripItem.image = child.nativeView as Image;
176+
} else {
177+
console.log(`[tabStripItem.insert] OTHER [${parent} > ${child} @${atIndex}] => [${parent.childNodes}]`);
178+
if (enableDebugLogging) {
179+
warn(
180+
`Unable to add child "${child.nativeView.constructor.name}" to <tabStripItem> as it does not have a nodeRole specified; ` +
181+
'please set a nodeRole of "label", or "image".'
182+
);
183+
}
184+
}
185+
},
186+
remove(child: NSVElement, parent: NSVElement<TabStripItem>): void {
187+
const tabStripItem = parent.nativeView;
188+
189+
if (child.nodeRole === 'label') {
190+
// WARNING: It is not evident from the implementation that TabStripItem supports removing label at all!
191+
tabStripItem.label = null;
192+
} else if (child.nodeRole === 'image') {
193+
// WARNING: It is not evident from the implementation that TabStripItem supports removing image at all!
194+
tabStripItem.image = null;
195+
} else {
196+
if (enableDebugLogging) {
197+
warn(
198+
`Unable to remove child "${child.nativeView.constructor.name}" from <tabStripItem> as it does not have a nodeRole specified; ` +
199+
'please set a nodeRole of "label", or "image"'
200+
);
201+
}
202+
}
203+
}
204+
}
205+
}
206+
);
207+
208+
registerElement(
209+
'tabContentItem',
210+
// @ts-ignore I can assure that this really does extend ViewBase
211+
() => TabContentItem,
212+
{ viewFlags: NSVViewFlags.CONTENT_VIEW }
213+
);
214+
215+
installed = true;
216+
}

src/core/tab-navigation-base/tab-navigation-base/react/index.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)