Skip to content

Feat/change tab controller center selected logic #1103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jan 21, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ca6b157
Move TabBar to functional component
M-i-k-e-l Dec 27, 2020
2377b58
Fix Fader in RTL
M-i-k-e-l Dec 27, 2020
6b907ce
Merge branch 'master' into typescript/tab-controller-move-to-function…
M-i-k-e-l Dec 29, 2020
ecbc6e0
change tab controller center selected logic (and move it to a helper…
M-i-k-e-l Dec 29, 2020
3f268b1
Fix import (cycle) + generate types
M-i-k-e-l Dec 29, 2020
cd65641
Change the DYNAMIC logic (did not look good when scrolling pages)
M-i-k-e-l Dec 29, 2020
8aaccdd
Merge branch 'master' into feat/change-tab-controller-center-selected…
M-i-k-e-l Jan 3, 2021
d79af43
Revert merging wrong commit
M-i-k-e-l Jan 3, 2021
462647b
FocusItemsHelper --> useFocusItemsHelper
M-i-k-e-l Jan 4, 2021
6b8c9ea
Add (and use) useScrollEnabler and useScrollReached hooks
M-i-k-e-l Jan 4, 2021
57d42f0
Move hooks to the hooks folder
M-i-k-e-l Jan 6, 2021
ae027dc
Merge branch 'master' into feat/change-tab-controller-center-selected…
M-i-k-e-l Jan 6, 2021
c7a677e
sideSpacing --> outerSpacing
M-i-k-e-l Jan 6, 2021
7d7c8e6
Fix typo
M-i-k-e-l Jan 6, 2021
c09acd8
renderSelectedIndicator --> selectedIndicator
M-i-k-e-l Jan 6, 2021
c2bf678
Move useFocusItemsHelper
M-i-k-e-l Jan 6, 2021
7e25406
Merge branch 'master' into feat/change-tab-controller-center-selected…
M-i-k-e-l Jan 18, 2021
60511a6
Rename useFocusItemsHelper --> useScrollIToItem
M-i-k-e-l Jan 18, 2021
9ace954
Fix typo
M-i-k-e-l Jan 18, 2021
c3697e0
Create useScrollTo hook
M-i-k-e-l Jan 18, 2021
f9f5c97
Refactor Props and ResultProps --> better names
M-i-k-e-l Jan 18, 2021
a7a9d82
Merge branch 'master' into feat/change-tab-controller-center-selected…
ethanshar Jan 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions demo/src/screens/componentScreens/FaderScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import _ from 'lodash';
import React, {Component} from 'react';
import {StyleSheet, ScrollView} from 'react-native';
import {Colors, Text, View, Fader, withScrollReached, WithScrollReachedProps} from 'react-native-ui-lib';
// @ts-ignore
import {renderHeader} from '../ExampleScreenPresenter';

const numberOfItems = 3;
Expand All @@ -10,6 +11,7 @@ const itemWidth = 100;
const itemHeight = 100;
const tintColor = undefined;

// @ts-ignore
const horizontal = faderPosition === Fader.position.START || faderPosition === Fader.position.END;

class FaderScreen extends Component<WithScrollReachedProps> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,15 @@ class TabControllerScreen extends Component<{}, State> {
this.state.items = this.generateTabItems();
}

generateTabItems = (fewItems = this.state.fewItems, centerSelected = this.state.centerSelected): TabControllerItemProps[] => {
generateTabItems = (fewItems = this.state.fewItems): TabControllerItemProps[] => {
let items: TabControllerItemProps[] = _.chain(TABS)
.take(fewItems ? 3 : TABS.length)
.map<TabControllerItemProps>(tab => ({label: tab, key: tab}))
.value();

const addItem: TabControllerItemProps = {icon: Assets.icons.demo.add, key: 'add', ignore: true, width: 60, onPress: this.onAddItem};

if (!centerSelected) {
items = [...items, addItem];
}
return items;
return [...items, addItem];
};

componentDidMount() {
Expand Down Expand Up @@ -83,7 +80,7 @@ class TabControllerScreen extends Component<{}, State> {
toggleCenterSelected = () => {
const {fewItems, centerSelected} = this.state;
this.setState({
items: this.generateTabItems(fewItems, !centerSelected),
items: this.generateTabItems(fewItems),
centerSelected: !centerSelected,
key: Date.now()
});
Expand Down
23 changes: 23 additions & 0 deletions generatedTypes/commons/useScrollEnabler.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { LayoutChangeEvent } from 'react-native';
export declare type Props = {
/**
* Whether the scroll is horizontal (default is false).
*/
horizontal?: boolean;
};
export declare type ResultProps = {
/**
* onContentSizeChange callback (should be set to your onContentSizeChange).
*/
onContentSizeChange: (contentWidth: number, contentHeight: number) => void;
/**
* onLayout callback (should be set to your onLayout).
*/
onLayout: (event: LayoutChangeEvent) => void;
/**
* Whether the scroll should be enabled (should be set to your scrollEnabled).
*/
scrollEnabled: boolean;
};
declare const useScrollEnabler: (props?: Props) => ResultProps;
export default useScrollEnabler;
28 changes: 28 additions & 0 deletions generatedTypes/commons/useScrollReached.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
export declare type Props = {
/**
* Whether the scroll is horizontal (default is false).
*/
horizontal?: boolean;
/**
* Allows to be notified prior to actually reaching the start \ end of the scroll (by the threshold).
* Should be a positive value.
*/
threshold?: number;
};
export declare type ResultProps = {
/**
* onScroll callback (should be set to your onScroll).
*/
onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
/**
* Is the scroll at the start (or equal\smaller than the threshold if one was given)
*/
isScrollAtStart?: boolean;
/**
* Is the scroll at the end (or equal\greater than the threshold if one was given)
*/
isScrollAtEnd?: boolean;
};
declare const useScrollReached: (props?: Props) => ResultProps;
export default useScrollReached;
3 changes: 2 additions & 1 deletion generatedTypes/components/fader/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/// <reference types="react" />
import { ImageProps } from '../image';
export declare enum FaderPosition {
/**
* @deprecated please use START instead
Expand All @@ -13,7 +14,7 @@ export declare enum FaderPosition {
TOP = "TOP",
BOTTOM = "BOTTOM"
}
export declare type FaderProps = {
export declare type FaderProps = Pick<ImageProps, 'supportRTL'> & {
/**
* Whether the fader is visible (default is true)
*/
Expand Down
4 changes: 3 additions & 1 deletion generatedTypes/components/tabController/FadedScrollView.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React from 'react';
import { ViewProps, ScrollViewProps } from 'react-native';
import { ForwardRefInjectedProps } from '../../commons/forwardRef';
export declare type FadedScrollViewProps = ViewProps & ScrollViewProps & {
children?: React.ReactNode | React.ReactNode[];
};
declare const _default: React.ComponentClass<FadedScrollViewProps, any> | React.FunctionComponent<FadedScrollViewProps>;
declare type Props = FadedScrollViewProps & ForwardRefInjectedProps;
declare const _default: React.ComponentType<Props>;
export default _default;
7 changes: 3 additions & 4 deletions generatedTypes/components/tabController/TabBarItem.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PureComponent } from 'react';
import { TextStyle, LayoutRectangle, LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';
import { /* processColor, */ TextStyle, LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';
import _ from 'lodash';
import Reanimated from 'react-native-reanimated';
import { State } from 'react-native-gesture-handler';
Expand Down Expand Up @@ -86,7 +86,7 @@ interface Props extends TabControllerItemProps {
targetPage: any;
state: State;
currentPage: Reanimated.Adaptable<number>;
onLayout: (layout: Partial<LayoutRectangle>, index: number) => void;
onLayout?: (event: LayoutChangeEvent, index: number) => void;
}
/**
* @description: TabController's TabBarItem
Expand All @@ -102,8 +102,7 @@ export default class TabBarItem extends PureComponent<Props> {
private itemWidth?;
private itemRef;
constructor(props: Props);
onStateChange: (...args: any[]) => void;
onLayout: ({ nativeEvent: { layout: { width } } }: LayoutChangeEvent) => void;
onLayout: (event: LayoutChangeEvent) => void;
onPress: () => void;
getItemStyle(): any[];
getLabelStyle(): (TextStyle | _.Dictionary<Reanimated.Node<number> | Reanimated.Node<string | number | boolean> | Reanimated.Node<"normal" | "bold" | "100" | "200" | "300" | "400" | "500" | "600" | "700" | "800" | "900"> | undefined> | undefined)[];
Expand Down
56 changes: 56 additions & 0 deletions generatedTypes/helpers/useFocusItemHelper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { RefObject } from 'react';
import { LayoutChangeEvent, ScrollView, FlatList } from 'react-native';
export declare enum OffsetType {
CENTER = "CENTER",
DYNAMIC = "DYNAMIC",
LEFT = "LEFT",
RIGHT = "RIGHT"
}
export declare type Props = {
/**
* A reference to the ScrollView (or FlatList) which the items are in
*/
scrollViewRef: RefObject<ScrollView | FlatList>;
/**
* The number of items
*/
itemsCount: number;
/**
* The selected item's index
*/
selectedIndex?: number;
/**
* Where would the item be located (default to CENTER)
*/
offsetType?: OffsetType;
/**
* Add a margin to the offset (default to true)
* This gives a better UX
* Not relevant to OffsetType.CENTER
*/
addOffsetMargin?: boolean;
/**
* How much space (padding \ margin) is there on he left\right of the items
*/
sideSpacing?: number;
/**
* How much space (padding \ margin) is there between each item
*/
innerSpacing?: number;
};
export declare type ResultProps = {
/**
* This should be called by each ot the items' onLayout
*/
onItemLayout: (event: LayoutChangeEvent, index: number) => void;
/**
* The items' width
*/
itemsWidths: number[];
/**
* Use in order to focus the item with the specified index
*/
focusIndex: (index: number, animated?: boolean) => void;
};
declare const useFocusItemsHelper: (props: Props) => ResultProps;
export default useFocusItemsHelper;
6 changes: 6 additions & 0 deletions src/commons/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,15 @@ module.exports = {
get withScrollEnabler() {
return require('./withScrollEnabler').default;
},
get useScrollEnabler() {
return require('./useScrollEnabler').default;
},
get withScrollReached() {
return require('./withScrollReached').default;
},
get useScrollReached() {
return require('./useScrollReached').default;
},
get modifiers() {
return require('./modifiers');
}
Expand Down
73 changes: 73 additions & 0 deletions src/commons/useScrollEnabler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {useState, useCallback, useRef} from 'react';
import {LayoutChangeEvent} from 'react-native';

export type Props = {
/**
* Whether the scroll is horizontal (default is false).
*/
horizontal?: boolean;
};

export type ResultProps = {
/**
* onContentSizeChange callback (should be set to your onContentSizeChange).
*/
onContentSizeChange: (contentWidth: number, contentHeight: number) => void;
/**
* onLayout callback (should be set to your onLayout).
*/
onLayout: (event: LayoutChangeEvent) => void;
/**
* Whether the scroll should be enabled (should be set to your scrollEnabled).
*/
scrollEnabled: boolean;
};

const useScrollEnabler = (props: Props = {}): ResultProps => {
const {horizontal = false} = props;
const [scrollEnabled, setScrollEnabled] = useState(true);
const contentSize = useRef<number>(0);
const layoutSize = useRef<number>(0);

const checkScroll = useCallback(() => {
const isScrollEnabled = contentSize.current > layoutSize.current;
if (isScrollEnabled !== scrollEnabled) {
setScrollEnabled(isScrollEnabled);
}
}, [scrollEnabled]);

const onContentSizeChange = useCallback((contentWidth: number, contentHeight: number) => {
const size = horizontal ? contentWidth : contentHeight;
if (size !== contentSize.current) {
contentSize.current = size;
if (layoutSize.current > 0) {
checkScroll();
}
}
},
[horizontal, checkScroll]);

const onLayout = useCallback((event: LayoutChangeEvent) => {
const {
nativeEvent: {
layout: {width, height}
}
} = event;
const size = horizontal ? width : height;
if (size !== layoutSize.current) {
layoutSize.current = size;
if (contentSize.current > 0) {
checkScroll();
}
}
},
[horizontal, checkScroll]);

return {
onContentSizeChange,
onLayout,
scrollEnabled
};
};

export default useScrollEnabler;
66 changes: 66 additions & 0 deletions src/commons/useScrollReached.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {useState, useCallback} from 'react';
import {NativeSyntheticEvent, NativeScrollEvent} from 'react-native';
import {Constants} from '../helpers';

export type Props = {
/**
* Whether the scroll is horizontal (default is false).
*/
horizontal?: boolean;
/**
* Allows to be notified prior to actually reaching the start \ end of the scroll (by the threshold).
* Should be a positive value.
*/
threshold?: number;
};

export type ResultProps = {
/**
* onScroll callback (should be set to your onScroll).
*/
onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
/**
* Is the scroll at the start (or equal\smaller than the threshold if one was given)
*/
isScrollAtStart?: boolean;
/**
* Is the scroll at the end (or equal\greater than the threshold if one was given)
*/
isScrollAtEnd?: boolean;
};

const DEFAULT_THRESHOLD = Constants.isAndroid ? 1 : 0;

const useScrollReached = (props: Props = {}): ResultProps => {
const {horizontal = false, threshold = DEFAULT_THRESHOLD} = props;
const [isScrollAtStart, setScrollAtStart] = useState(true);
const [isScrollAtEnd, setScrollAtEnd] = useState(false);

const onScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
const {
nativeEvent: {
layoutMeasurement: {width: layoutWidth, height: layoutHeight},
contentOffset: {x: offsetX, y: offsetY},
contentSize: {width: contentWidth, height: contentHeight}
}
} = event;

const layoutSize = horizontal ? layoutWidth : layoutHeight;
const offset = horizontal ? offsetX : offsetY;
const contentSize = horizontal ? contentWidth : contentHeight;
const closeToStart = offset <= threshold;
if (closeToStart !== isScrollAtStart) {
setScrollAtStart(closeToStart);
}

const closeToEnd = layoutSize + offset >= contentSize - threshold;
if (closeToEnd !== isScrollAtEnd) {
setScrollAtEnd(closeToEnd);
}
},
[horizontal, threshold, isScrollAtStart, isScrollAtEnd]);

return {onScroll, isScrollAtStart, isScrollAtEnd};
};

export default useScrollReached;
Loading