From de3376a2ee8d9ff1ae03e71ef8d7a0cc87361b8b Mon Sep 17 00:00:00 2001 From: sstimac Date: Wed, 12 Oct 2022 14:13:48 +0200 Subject: [PATCH 1/2] update tab menu with better scrolling autonomy & styling (#754) --- components/TabMenu/TabMenu.js | 78 +++++++++++++++++++++++++++++-- components/TabMenu/TabMenuItem.js | 17 +++++-- theme.js | 3 +- 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/components/TabMenu/TabMenu.js b/components/TabMenu/TabMenu.js index 9dbc8dce..431b7188 100644 --- a/components/TabMenu/TabMenu.js +++ b/components/TabMenu/TabMenu.js @@ -12,26 +12,91 @@ class TabMenu extends PureComponent { super(props); autoBindReact(this); + + this.state = { + itemWidths: {}, + scrollOffset: 0, + }; } - handleOptionSelected(option) { + handleOptionSelected(key, option) { const { onOptionSelected } = this.props; + const { scrollOffset, itemWidths, viewportHorizontal } = this.state; + + let itemStart = 0; + + _.forEach(itemWidths, (itemWidth, index) => { + if (index < key) { + itemStart += itemWidth; + } + }); + + const itemEnd = itemStart + itemWidths[key]; LayoutAnimation.easeInEaseOut(); onOptionSelected(option); + + if (itemEnd > _.last(viewportHorizontal)) { + const extraScroll = itemEnd - _.last(viewportHorizontal); + + this.scroll.scrollTo({ + x: extraScroll + scrollOffset, + y: 0, + animated: true, + }); + } + + if (itemStart < _.head(viewportHorizontal)) { + const extraScroll = _.head(viewportHorizontal) - itemStart; + + this.scroll.scrollTo({ + x: scrollOffset - extraScroll, + y: 0, + animated: true, + }); + } + } + + handleItemLayout(key, width) { + const { itemWidths } = this.state; + + this.setState({ + itemWidths: { + ...itemWidths, + [key]: width, + }, + }); + } + + handleScroll({ + nativeEvent: { + contentOffset: { x }, + layoutMeasurement: { width }, + }, + }) { + this.setState({ scrollOffset: x, viewportHorizontal: [x, x + width] }); + } + + handleScrollLayout({ + nativeEvent: { + layout: { width }, + }, + }) { + this.setState({ viewportHorizontal: [0, width] }); } - renderOption(option) { + renderOption(option, key) { const { selectedOption } = this.props; const isSelected = selectedOption && option.title === selectedOption.title; return ( this.handleOptionSelected(key, option)} + onLayoutMeasured={width => this.handleItemLayout(key, width)} /> ); } @@ -43,6 +108,11 @@ class TabMenu extends PureComponent { { + this.scroll = ref; + }} showsHorizontalScrollIndicator={false} style={style.list} > diff --git a/components/TabMenu/TabMenuItem.js b/components/TabMenu/TabMenuItem.js index 6a9e5fde..32cff9e8 100644 --- a/components/TabMenu/TabMenuItem.js +++ b/components/TabMenu/TabMenuItem.js @@ -1,7 +1,6 @@ import React, { PureComponent } from 'react'; import { LayoutAnimation } from 'react-native'; import autoBindReact from 'auto-bind/react'; -import _ from 'lodash'; import PropTypes from 'prop-types'; import { connectStyle } from '@shoutem/theme'; import { Text } from '../Text'; @@ -26,7 +25,7 @@ class TabMenuItem extends PureComponent { onItemPressed(item); } - handleLayout({ + handleTextLayout({ nativeEvent: { layout: { width }, }, @@ -35,6 +34,16 @@ class TabMenuItem extends PureComponent { this.setState({ baseWidth: width }); } + handleContainerLayout({ + nativeEvent: { + layout: { width }, + }, + }) { + const { onLayoutMeasured } = this.props; + + onLayoutMeasured(width); + } + render() { const { style, isSelected, item } = this.props; const { baseWidth } = this.state; @@ -42,11 +51,12 @@ class TabMenuItem extends PureComponent { return ( {item.title} @@ -58,6 +68,7 @@ class TabMenuItem extends PureComponent { TabMenuItem.propTypes = { style: PropTypes.object.isRequired, + onLayoutMeasured: PropTypes.func.isRequired, isSelected: PropTypes.bool, item: optionShape, onItemPressed: PropTypes.func, diff --git a/theme.js b/theme.js index 647bcb0c..e7b2bfeb 100644 --- a/theme.js +++ b/theme.js @@ -1,4 +1,4 @@ -import { Dimensions, Platform, StatusBar, StyleSheet } from 'react-native'; +import { Dimensions, Platform, StyleSheet } from 'react-native'; import { changeColorAlpha, createSharedStyle, @@ -2899,7 +2899,6 @@ export default () => { borderRadius: 1, flexDirection: 'row', marginBottom: 8, - marginLeft: 8, }, text: { marginTop: 12, From 5a4ad0ae0b849c5b3ad36d9310d9db18387697a5 Mon Sep 17 00:00:00 2001 From: Slavko Date: Wed, 12 Oct 2022 14:15:44 +0200 Subject: [PATCH 2/2] bump version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0dded2ac..402e801b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@shoutem/ui", - "version": "5.4.0", + "version": "5.6.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@shoutem/ui", - "version": "5.4.0", + "version": "5.6.0", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { diff --git a/package.json b/package.json index b3c004fc..083a9eed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@shoutem/ui", - "version": "5.5.0", + "version": "5.6.0", "description": "Styleable set of components for React Native applications", "scripts": { "lint": "eslint .",