extended canvas, dualscreeninfo, auto replace

This commit is contained in:
keilaloia 2020-08-20 18:41:54 -07:00
Родитель edee852ed1
Коммит b5471cb85e
17 изменённых файлов: 173 добавлений и 125 удалений

6
twopane-navigation/example/package-lock.json сгенерированный
Просмотреть файл

@ -11222,9 +11222,9 @@
"dev": true
},
"twopane-navigation": {
"version": "1.0.9",
"resolved": "http://localhost:4873/twopane-navigation/-/twopane-navigation-1.0.9.tgz",
"integrity": "sha512-puGKCxn2MYTL8UVKdHnV0ln7ENh4nWtrpqOq+yV7xl05ewkRNfm31d+/tAfQvdb9tlnLY5TRrwifa7nJtLlMfg==",
"version": "1.0.13",
"resolved": "http://localhost:4873/twopane-navigation/-/twopane-navigation-1.0.13.tgz",
"integrity": "sha512-4h0uYvcY22sROEkuzYQOulW5FOEc9XCYNYxiiIcJdlMf5iQ9tCYlnDmuhAj0NPW/+vSAthWKg0t2qf9lajsbDw==",
"requires": {
"lodash": "*",
"react-native-dualscreeninfo": "^0.1.3",

Просмотреть файл

@ -14,8 +14,9 @@
"dependencies": {
"react": "16.13.1",
"react-native": "0.63.2",
"react-native-dualscreeninfo": "^0.1.3",
"react-native-vector-icons": "^7.0.0",
"twopane-navigation": "^1.0.8"
"twopane-navigation": "^1.0.13"
},
"devDependencies": {
"@babel/core": "^7.8.4",

Просмотреть файл

@ -74,7 +74,6 @@ const RestaurantDetails: IRestaurantDetails[] =
}
]
const App = () => {
return (
<TwoPaneApp

Просмотреть файл

@ -51,9 +51,9 @@ const LocationInformation = (props: ILocationInformationProps) =>{
<View>
<View>
<Button title='Order now' color='#D26441'
onPress={()=> autoPane.AddOrMoveToFront(`checkout ${details.name}`,
onPress={()=> autoPane.Add(`checkout ${details.name}`,
<ShoppingCart />,
{title: 'Checkout'},
undefined,
true,true)}/>
</View>
<TouchableOpacity onPress={()=> onePane.AddExtended(`directions ${details.name}`,

Просмотреть файл

@ -6,7 +6,7 @@ export interface IShoppingCartProps {
const ShoppingCart = (props: IShoppingCartProps) => {
return (
<Text>HELLO WORLD FROM SHOPPING CART</Text>
<Text>HELLO WORLD FROM SHOPPING CART{Math.random()}</Text>
);
}

Просмотреть файл

@ -1,7 +1,7 @@
{
"name": "twopane-navigation",
"Title": "twopane-navigation",
"version": "1.0.9",
"version": "1.0.13",
"description": "React Native package for dual screen devices navigation support (Surface Duo)",
"react-native": "src/index.ts",
"types": "lib/typescript/index.d.ts",

Просмотреть файл

@ -8,48 +8,59 @@ import { WindowRect } from "react-native-dualscreeninfo";
import { getUtilityStore } from "../../shared/utilityStore/utilityStore.selectors";
interface IPaneRendererProps {
prependKey: string;
paneComponent: IPaneComponent[];
paneRects: WindowRect[];
}
const PaneRenderer = (props: IPaneRendererProps) => {
const defaultConfig = getUtilityStore().config;
const { prependKey, paneComponent, paneRects } = props;
const { paneComponent, paneRects } = props;
const isGoBackOne = paneComponent.filter(x => x.pane === paneType.ONE).length > 1;
const isGoBackTwo = paneComponent.filter(x => x.pane === paneType.TWO).length > 1;
const paneStyling = (_paneType: paneType, _isExtended:boolean, _paneRects: WindowRect[]) => {
if(_paneType === paneType.TWO && _paneRects.length > 1){
return Object.assign({},twoPaneStyles(paneRects[1]).twoPane, defaultConfig?.twoPane?.paneBody!)
} else if (_paneType === paneType.ONE && !_isExtended) {
return Object.assign({},onePaneStyles(paneRects[0]).onePane, defaultConfig?.onePane?.paneBody!)
} else if (_paneType === paneType.ONE && _isExtended) {
return Object.assign({},onePaneStyles(paneRects[0]).extendedPane, defaultConfig?.onePane?.paneBody!)
}
}
return (
<Fragment>
{
paneComponent.map((val: IPaneComponent) =>
<View key={prependKey + val.key}
style={[generalStyles.container, paneStyling(val.pane,val.isExtended, paneRects)]}>
<View style={generalStyles.header}>
<PaneHeaderContainer
isGoBack={(val.pane === paneType.ONE ? isGoBackOne : isGoBackTwo)}
screenHeader={val.header}
goBack={() => (val.pane === paneType.ONE ? onePane.GoBack() : twoPane.GoBack())}
configDefaultHeader={val.pane === paneType.ONE ? defaultConfig.onePane?.paneHeader! : defaultConfig.twoPane?.paneHeader!}
/>
</View>
<View key={val.key}>
{ (val.pane === paneType.ONE ) &&
<View
style={generalStyles.body}>
{val.paneElement}
style={(val.isExtended) ?
Object.assign({},onePaneStyles(paneRects[0]).extendedPane, defaultConfig?.onePane?.paneBody!) :
Object.assign({},onePaneStyles(paneRects[0]).onePane, defaultConfig?.onePane?.paneBody!)}>
<View style={generalStyles.header}>
<PaneHeaderContainer
isGoBack={ isGoBackOne}
screenHeader={val.header}
goBack={() => (onePane.GoBack())}
configDefaultHeader={defaultConfig.onePane?.paneHeader!}
/>
</View>
<View
style={generalStyles.body}>
{val.paneElement}
</View>
</View>
</View>
}
{(val.pane === paneType.TWO && paneRects.length > 1 ) &&
<View
style={Object.assign({},twoPaneStyles(paneRects[1]).twoPane, defaultConfig?.twoPane?.paneBody!)}>
<View style={generalStyles.header}>
<PaneHeaderContainer
isGoBack={isGoBackTwo}
screenHeader={val.header}
goBack={() => (twoPane.GoBack())}
configDefaultHeader={defaultConfig.twoPane?.paneHeader!}
/>
</View>
<View
style={generalStyles.body}>
{val.paneElement}
</View>
</View>
}
</View>
)
}
</Fragment >
@ -57,11 +68,6 @@ const PaneRenderer = (props: IPaneRendererProps) => {
}
const generalStyles = StyleSheet.create({
container: {
flex: 1,
...StyleSheet.absoluteFillObject,
color: 'black'
},
header: {
height: '10%'
},
@ -72,11 +78,17 @@ const generalStyles = StyleSheet.create({
const onePaneStyles = (paneRects : WindowRect) => StyleSheet.create({
onePane: {
flex: 1,
...StyleSheet.absoluteFillObject,
color: 'black',
left: paneRects.x,
height: paneRects.height,
width: paneRects.width
},
extendedPane: {
flex: 1,
...StyleSheet.absoluteFillObject,
color: 'black',
left: paneRects.x,
height: paneRects.height,
width: paneRects.width * 2
@ -85,10 +97,13 @@ const onePaneStyles = (paneRects : WindowRect) => StyleSheet.create({
const twoPaneStyles = (paneRects : WindowRect) => StyleSheet.create({
twoPane: {
flex: 1,
...StyleSheet.absoluteFillObject,
color: 'black',
left: paneRects.x,
height: paneRects.height,
width: paneRects.width,
},
}
});
export default PaneRenderer;

Просмотреть файл

@ -2,12 +2,13 @@ import React, { Fragment } from 'react';
import DualApp from '../twoPaneApp';
import { IPaneComponent } from '../../../utilities/interfaces';
import { render, toJSON } from '@testing-library/react-native';
import * as dsInfo from 'react-native-dualscreeninfo';
describe('DualApp Tests', () => {
it('should render singleScreen', () => {
// Arrange
jest.useFakeTimers();
const _onePaneDefault: IPaneComponent = {
key: 'singleDefault',
paneElement: <Fragment />,

Просмотреть файл

@ -15,18 +15,18 @@ exports[`DualApp Tests should render singleScreen 1`] = `
style={
Array [
Object {
"backgroundColor": "black",
"bottom": 0,
"color": "black",
"flex": 1,
"height": 720,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
"width": 540,
},
Object {
"height": 720,
"left": 0,
"width": 540,
},
]
}
@ -54,10 +54,13 @@ exports[`DualApp Tests should render singleScreen 1`] = `
"flexDirection": "row",
"justifyContent": "flex-start",
},
Object {
"backgroundColor": "#212121",
"paddingHorizontal": 25,
},
Array [
Object {
"backgroundColor": "#212121",
"paddingHorizontal": 25,
},
undefined,
],
]
}
>
@ -88,18 +91,18 @@ exports[`DualApp Tests should render singleScreen 1`] = `
style={
Array [
Object {
"backgroundColor": "black",
"bottom": 0,
"color": "black",
"flex": 1,
"height": 720,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
"width": 540,
},
Object {
"left": 570,
"height": 720,
"left": 573.6,
"width": 540,
},
]
}
@ -127,10 +130,13 @@ exports[`DualApp Tests should render singleScreen 1`] = `
"flexDirection": "row",
"justifyContent": "flex-start",
},
Object {
"backgroundColor": "#212121",
"paddingHorizontal": 25,
},
Array [
Object {
"backgroundColor": "#212121",
"paddingHorizontal": 25,
},
undefined,
],
]
}
>

Просмотреть файл

@ -1,4 +1,4 @@
import React, { useMemo, useEffect, useState } from 'react';
import React, { useMemo, useEffect, useState, Fragment, useRef } from 'react';
import { View } from 'react-native';
import { IPaneComponent } from '../../utilities/interfaces';
@ -20,7 +20,7 @@ const TwoPaneHub = () => {
const headerState: IHeaderState = getHeaderSelector();
const keyState = getScreenKeyState();
const [paneRects, setPaneRects] = useState<WindowRect[]>(DualScreenInfo.windowRects);
const [paneRects, setPaneRects] = useState<WindowRect[]>([] as WindowRect[]);
useEffect(() => {
utilityStore.isTwoPane(DualScreenInfo.isSpanning)
@ -45,41 +45,29 @@ const TwoPaneHub = () => {
const _handleSpanningChanged = (update: DualScreenInfoPayload) => {
utilityStore.isTwoPane(update.isSpanning)
if (update.isSpanning) {
if(paneRects.length < 2)
{
setPaneRects(DualScreenInfo.windowRects)
setPaneRects(update.windowRects)
}
onePane.mergeToOppositeScreen();
} else {
if(paneRects.length < 1) {
setPaneRects(update.windowRects)
}
twoPane.mergeToOppositeScreen();
}
};
const fakeWindowRect: WindowRect[] = [
{
x: 0,
y: 0,
width: 540,
height: 720
},
{
x: 573.6,
y: 0,
width: 540,
height: 720
}
]
return (
<View>
<PaneRenderer
prependKey={''}
paneComponent={screenStack}
paneRects={fakeWindowRect}/>
</View>
<Fragment>
{ paneRects.length > 0 &&
<PaneRenderer
paneComponent={screenStack}
paneRects={paneRects}/>
}
</Fragment>
);
};

Просмотреть файл

@ -9,38 +9,51 @@ import { ReactElement } from 'react';
import { IHeader } from '../../shared/screenStore/headerStore/header.interface';
import { IKeyState, IKeyObject } from '../../shared/screenStore/keyStore/key.interface';
const AddPaneElement= (key: string, element: ReactElement, header?: IHeader, isMerge: boolean = false, isExtended:boolean = false) => {
store.dispatch(pushKey(paneType.ONE, key, isMerge, isExtended));
store.dispatch(pushElement(`${paneType.ONE}_${key}`, element));
if (header) {
store.dispatch(pushHeader(`${paneType.ONE}_${key}`, header));
}
}
/**
* Pushes element to the top of the onePane stack
* Pushes element to the top of the onePane stack or replaces the original with the new element
*/
const Add = (key: string, element: ReactElement, header?: IHeader, isMerge: boolean = false) => {
store.dispatch(pushKey(paneType.ONE, key, isMerge));
store.dispatch(pushElement(`${paneType.ONE}_${key}`, element));
if (header) {
store.dispatch(pushHeader(`${paneType.ONE}_${key}`, header));
const keys: IKeyState = store.getState().KeyReducers;
const index = keys.keys.findIndex(val => val.key === `${paneType.ONE}_${key}`);
if(index > -1) {
//remove old entry
store.dispatch(removeHeaderByKey(keys.keys[index].key))
store.dispatch(removePaneElementByKey(keys.keys[index].key))
// push new entry
store.dispatch(pushElement(`${paneType.ONE}_${key}`, element));
if (header) {
store.dispatch(pushHeader(`${paneType.ONE}_${key}`, header));
}
store.dispatch(moveToFront(paneType.ONE, `${paneType.ONE}_${key}`));
} else {
AddPaneElement(key, element, header, isMerge)
}
};
/**
* Pushes element to the top of the onePane stack
* Pushes element to the top of the onePane stack or moves the original to the top of the stack
*/
const AddExtended = (key: string, element: ReactElement, header?: IHeader) => {
store.dispatch(pushKey(paneType.ONE, key, false, true));
store.dispatch(pushElement(`${paneType.ONE}_${key}`, element));
if (header) {
store.dispatch(pushHeader(`${paneType.ONE}_${key}`, header));
}
AddPaneElement(key, element, header, false, true)
};
/**
* Pushes element to the top of the onePane stack or if the key is already in the stack,
move that key to the top of the stack
* Pushes element to the top of the onePane stack or moves the original to the top of the stack
*/
const AddOrMoveToFront = (key: string, element: React.ReactElement, header?: IHeader, isMerge: boolean = false) => {
const keys: IKeyState = store.getState().KeyReducers;
const onePaneState: IKeyObject[] = keys.keys.filter(x => x.screen === paneType.ONE)
if (!onePaneState.some(val => val.key === `${paneType.ONE}_${key}`)) {
Add(key, element, header, isMerge)
AddPaneElement(key, element, header, isMerge)
} else {
store.dispatch(moveToFront(paneType.ONE, `${paneType.ONE}_${key}`));
}

Просмотреть файл

@ -199,12 +199,12 @@ describe('onePane methods', () => {
const expectedState: IKeyState =
{
keys:
[{ key: 'ONE_test2', isMerge: false, screen: paneType.ONE },
{ key: 'ONE_ONE_first', isMerge: false, screen: paneType.ONE },
{ key: 'ONE_ONE_second', isMerge: false, screen: paneType.ONE },
{ key: 'ONE_test3', isMerge: true, screen: paneType.TWO },
{ key: 'ONE_test5', isMerge: true, screen: paneType.TWO },
{ key: 'ONE_test1', isMerge: true, screen: paneType.TWO }]
[{ key: 'ONE_test2', isMerge: false, screen: paneType.ONE, isExtended: false},
{ key: 'ONE_ONE_first', isMerge: false, screen: paneType.ONE, isExtended: false },
{ key: 'ONE_ONE_second', isMerge: false, screen: paneType.ONE, isExtended: false },
{ key: 'ONE_test3', isMerge: true, screen: paneType.TWO, isExtended: false },
{ key: 'ONE_test5', isMerge: true, screen: paneType.TWO, isExtended: false },
{ key: 'ONE_test1', isMerge: true, screen: paneType.TWO, isExtended: false }]
}
const popScreenSpy = jest.spyOn(keyActions, 'popScreen')

Просмотреть файл

@ -37,7 +37,10 @@ const headerReducer = (
case REMOVE_KEY_HEADER: {
// using delete over lodash.omit to reduce dependencies on external libraries
const newData = state;
delete newData.headers[action.payload.key];
if(newData.headers[action.payload.key])
{
delete newData.headers[action.payload.key];
}
return { ...newData }
}
default:

Просмотреть файл

@ -37,7 +37,10 @@ const PaneElementReducer = (
case REMOVE_KEY_PANE_ELEMENT: {
// using delete over lodash.omit to reduce dependencies on external libraries
const newData = state;
delete newData.PaneElements[action.payload.key];
if(newData.PaneElements[action.payload.key])
{
delete newData.PaneElements[action.payload.key];
}
return { ...newData }
}
default:

Просмотреть файл

@ -200,12 +200,12 @@ describe('twoPane methods', () => {
const expectedState: IKeyState =
{
keys:
[{ key: 'TWO_test3', isMerge: true, screen: paneType.ONE },
{ key: 'TWO_test5', isMerge: true, screen: paneType.ONE },
{ key: 'TWO_test1', isMerge: true, screen: paneType.ONE },
{ key: 'TWO_test2', isMerge: false, screen: paneType.TWO },
{ key: 'TWO_TWO_first', isMerge: false, screen: paneType.TWO },
{ key: 'TWO_TWO_second', isMerge: false, screen: paneType.TWO }]
[{ key: 'TWO_test3', isMerge: true, screen: paneType.ONE, isExtended: false},
{ key: 'TWO_test5', isMerge: true, screen: paneType.ONE, isExtended: false },
{ key: 'TWO_test1', isMerge: true, screen: paneType.ONE, isExtended: false },
{ key: 'TWO_test2', isMerge: false, screen: paneType.TWO, isExtended: false },
{ key: 'TWO_TWO_first', isMerge: false, screen: paneType.TWO, isExtended: false },
{ key: 'TWO_TWO_second', isMerge: false, screen: paneType.TWO, isExtended: false }]
}
const popScreenSpy = jest.spyOn(keyActions, 'popScreen')

Просмотреть файл

@ -7,29 +7,49 @@ import { pushHeader, replaceHeader, removeHeaderByKey } from '../../shared/scree
import { pushElement, removePaneElementByKey, replacePaneElement } from '../../shared/screenStore/paneElementStore/paneElement.action';
import { IHeader } from '../../shared/screenStore/headerStore/header.interface';
import { IKeyState, IKeyObject } from '../../shared/screenStore/keyStore/key.interface';
import { ReactElement } from 'react';
const AddPaneElement= (key: string, element: ReactElement, header?: IHeader, isMerge: boolean = false) => {
store.dispatch(pushKey(paneType.TWO, key, isMerge));
store.dispatch(pushElement(`${paneType.TWO}_${key}`, element));
if (header) {
store.dispatch(pushHeader(`${paneType.TWO}_${key}`, header));
}
}
/**
* Pushes element to the top of the Twopane stack.
* Pushes element to the top of the twoPane stack or replaces the original with the new element
*/
const Add = (key: string, element: React.ReactElement, header?: IHeader, isMerge: boolean = false) => {
store.dispatch(pushKey(paneType.TWO, key, isMerge));
store.dispatch(pushElement(`${paneType.TWO}_${key}`, element));
if (header) {
store.dispatch(pushHeader(`${paneType.TWO}_${key}`, header));
const Add = (key: string, element: ReactElement, header?: IHeader, isMerge: boolean = false) => {
const keys: IKeyState = store.getState().KeyReducers;
const index = keys.keys.findIndex(val => val.key === `${paneType.TWO}_${key}`);
if(index > -1) {
//remove old entry
store.dispatch(removeHeaderByKey(keys.keys[index].key))
store.dispatch(removePaneElementByKey(keys.keys[index].key))
// push new entry
store.dispatch(pushElement(`${paneType.TWO}_${key}`, element));
if (header) {
store.dispatch(pushHeader(`${paneType.TWO}_${key}`, header));
}
store.dispatch(moveToFront(paneType.TWO, `${paneType.TWO}_${key}`));
} else {
AddPaneElement(key, element, header, isMerge)
}
};
/**
* Pushes element to the top of the twopane stack or if the key is already in the stack,
move that key to the top of the stack
* Pushes element to the top of the twoPane stack or moves the original to the top of the stack
*/
const AddOrMoveToFront = (key: string, element: React.ReactElement, header?: IHeader, isMerge: boolean = false,) => {
const AddOrMoveToFront = (key: string, element: ReactElement, header?: IHeader, isMerge: boolean = false,) => {
const keys: IKeyState = store.getState().KeyReducers;
const twoPaneState: IKeyObject[] = keys.keys.filter(x => x.screen === paneType.TWO)
if (!twoPaneState.some(val => val.key === `${paneType.TWO}_${key}`)) {
Add(key, element, header, isMerge)
AddPaneElement(key, element, header, isMerge)
} else {
store.dispatch(moveToFront(paneType.TWO, `${paneType.TWO}_${key}`));
}
@ -78,7 +98,7 @@ const GoBack = () => {
/**
* Replace the current element for this twoPane component
*/
const ReplaceScreen = (key: string, element: React.ReactElement) => {
const ReplaceScreen = (key: string, element: ReactElement) => {
store.dispatch(replacePaneElement(`${paneType.TWO}_${key}`, element))
}

Просмотреть файл

@ -35,7 +35,6 @@ export interface IEmptyAction {
export enum paneType {
ONE = 'ONE',
TWO = 'TWO',
EXTENDED = 'EXTENDED'
}
export interface ITwoPaneAppProps {