diff --git a/components/Icon/assets/checkbox-rectangle-off.svg b/components/Icon/assets/checkbox-rectangle-off.svg
new file mode 100644
index 00000000..ced67461
--- /dev/null
+++ b/components/Icon/assets/checkbox-rectangle-off.svg
@@ -0,0 +1,5 @@
+
diff --git a/components/Icon/assets/checkbox-rectangle-on.svg b/components/Icon/assets/checkbox-rectangle-on.svg
new file mode 100644
index 00000000..eba3dc93
--- /dev/null
+++ b/components/Icon/assets/checkbox-rectangle-on.svg
@@ -0,0 +1,3 @@
+
diff --git a/components/Icon/assets/index.js b/components/Icon/assets/index.js
index 943803d7..617408b8 100644
--- a/components/Icon/assets/index.js
+++ b/components/Icon/assets/index.js
@@ -13,6 +13,8 @@ import camSwitch from './cam-switch.svg';
import cart from './cart.svg';
import checkboxOff from './checkbox-off.svg';
import checkboxOn from './checkbox-on.svg';
+import checkbocRectangleOff from './checkbox-rectangle-off';
+import checkbocRectangleOn from './checkbox-rectangle-on';
import clearText from './clear-text.svg';
import close from './close.svg';
import comment from './comment.svg';
@@ -111,6 +113,8 @@ export const defaultConfig = [
{ name: 'cart', icon: cart },
{ name: 'checkbox-off', icon: checkboxOff },
{ name: 'checkbox-on', icon: checkboxOn },
+ { name: 'checkbox-rectangle-off', icon: checkbocRectangleOff },
+ { name: 'checkbox-rectangle-on', icon: checkbocRectangleOn },
{ name: 'clear-text', icon: clearText },
{ name: 'close', icon: close },
{ name: 'comment', icon: comment },
diff --git a/components/InlineDropDownMenu/InlineDropDownMenu.js b/components/InlineDropDownMenu/InlineDropDownMenu.js
new file mode 100644
index 00000000..27ac6677
--- /dev/null
+++ b/components/InlineDropDownMenu/InlineDropDownMenu.js
@@ -0,0 +1,130 @@
+import React, { PureComponent } from 'react';
+import autoBindReact from 'auto-bind/react';
+import { FlatList, Animated } from 'react-native';
+import _ from 'lodash';
+import PropTypes from 'prop-types';
+import { connectStyle } from '@shoutem/theme';
+import { TouchableOpacity } from '../TouchableOpacity';
+import { View } from '../View';
+import { Caption, Text } from '../Text';
+import { Icon } from '../Icon';
+import { InlineDropDownMenuItem } from './InlineDropDownMenuItem';
+
+const AnimatedIcon = Animated.createAnimatedComponent(Icon);
+
+const optionShape = PropTypes.shape({
+ title: PropTypes.string.isRequired,
+ key: PropTypes.string.isRequired,
+});
+
+class InlineDropDownMenu extends PureComponent {
+ static propTypes = {
+ heading: PropTypes.string,
+ selectedDescriptor: PropTypes.string,
+ options: PropTypes.arrayOf(optionShape).isRequired,
+ onOptionSelected: PropTypes.func,
+ selectedOption: optionShape,
+ };
+
+ constructor(props) {
+ super(props);
+
+ autoBindReact(this);
+
+ this.dropDownIconValue = new Animated.Value(0);
+
+ this.state = {
+ collapsed: false,
+ };
+ }
+
+ handleToggleMenuPress() {
+ const { collapsed } = this.state;
+
+ const toValue = collapsed ? 0 : 1;
+
+ this.setState(
+ { collapsed: !collapsed },
+ () => Animated.timing(
+ this.dropDownIconValue,
+ {
+ toValue,
+ useNativeDriver: true,
+ duration: 300,
+ }
+ ).start());
+ }
+
+ handleOptionPress(option) {
+ const { onOptionSelected } = this.props;
+
+ if (onOptionSelected) {
+ onOptionSelected(option);
+ }
+
+ this.handleToggleMenuPress();
+ }
+
+ renderOption({ item, index }) {
+ const { selectedOption, selectedDescriptor } = this.props;
+
+ const isSelected = selectedOption.key === item.key;
+
+ return (
+
+ )
+ }
+
+ render() {
+ const {
+ style,
+ heading,
+ selectedOption,
+ options,
+ } = this.props;
+ const { collapsed } = this.state;
+
+ return (
+
+
+ {heading}
+
+ {selectedOption?.title}
+
+
+
+ {collapsed && (
+
+ )}
+
+ );
+ }
+}
+
+const StyledComponent = connectStyle('shoutem.ui.InlineDropDownMenu')(
+ InlineDropDownMenu,
+);
+
+export { StyledComponent as InlineDropDownMenu };
diff --git a/components/InlineDropDownMenu/InlineDropDownMenuItem.js b/components/InlineDropDownMenu/InlineDropDownMenuItem.js
new file mode 100644
index 00000000..deadf676
--- /dev/null
+++ b/components/InlineDropDownMenu/InlineDropDownMenuItem.js
@@ -0,0 +1,85 @@
+import React, { PureComponent } from 'react';
+import autoBindReact from 'auto-bind/react';
+import { Animated, Dimensions } from 'react-native';
+import _ from 'lodash';
+import PropTypes from 'prop-types';
+import { connectStyle } from '@shoutem/theme';
+import { TouchableOpacity } from '../TouchableOpacity';
+import { Text } from '../Text';
+
+const window = Dimensions.get('window');
+const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity);
+
+class InlineDropDownMenuItem extends PureComponent {
+ static propTypes = {
+ item: PropTypes.object,
+ index: PropTypes.number,
+ selectedDescriptor: PropTypes.string,
+ onItemPressed: PropTypes.func,
+ isSelected: PropTypes.bool,
+ };
+
+ constructor(props) {
+ super(props);
+
+ autoBindReact(this);
+
+ this.animatedValue = new Animated.Value(0);
+ }
+
+ componentDidMount() {
+ const { index } = this.props;
+
+ Animated.timing(
+ this.animatedValue,
+ {
+ toValue: 1,
+ useNativeDriver: true,
+ duration: 300 + index * 15,
+ }
+ ).start();
+ }
+
+ handlePress() {
+ const { onItemPressed, item } = this.props;
+
+ if (onItemPressed) {
+ onItemPressed(item);
+ }
+ }
+
+ render() {
+ const { isSelected, selectedDescriptor, item, style } = this.props;
+
+ const resolvedText = isSelected ? `${item.title} (${selectedDescriptor})` : item.title;
+ const textStyle = isSelected ? 'muted' : '';
+
+ return (
+
+ {resolvedText}
+
+ );
+ }
+}
+
+const StyledComponent = connectStyle('shoutem.ui.InlineDropDownMenuItem')(
+ InlineDropDownMenuItem,
+);
+
+export { StyledComponent as InlineDropDownMenuItem };
diff --git a/components/InlineDropDownMenu/index.js b/components/InlineDropDownMenu/index.js
new file mode 100644
index 00000000..b95d8e64
--- /dev/null
+++ b/components/InlineDropDownMenu/index.js
@@ -0,0 +1,2 @@
+export { InlineDropDownMenu } from './InlineDropDownMenu';
+export { InlineDropDownMenuItem } from './InlineDropDownMenuItem';
diff --git a/components/TabMenu/TabMenu.js b/components/TabMenu/TabMenu.js
new file mode 100644
index 00000000..cb08b9bc
--- /dev/null
+++ b/components/TabMenu/TabMenu.js
@@ -0,0 +1,64 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import autoBindReact from 'auto-bind/react';
+import _ from 'lodash';
+import { ScrollView, LayoutAnimation } from 'react-native';
+import { connectStyle } from '@shoutem/theme';
+import { optionShape } from './const';
+import { TabMenuItem } from './TabMenuItem';
+
+class TabMenu extends PureComponent {
+ static propTypes = {
+ options: PropTypes.arrayOf(optionShape).isRequired,
+ onOptionSelected: PropTypes.func,
+ selectedOption: optionShape,
+ style: PropTypes.object,
+ };
+
+ constructor(props) {
+ super(props);
+
+ autoBindReact(this);
+ }
+
+ handleOptionSelected(option) {
+ const { onOptionSelected } = this.props;
+
+ LayoutAnimation.easeInEaseOut();
+ onOptionSelected(option);
+ }
+
+ renderOption(option) {
+ const { selectedOption } = this.props;
+
+ const isSelected = selectedOption && option.title === selectedOption.title;
+
+ return (
+
+ );
+ }
+
+ render() {
+ const { style, options } = this.props;
+
+ return (
+
+ {_.map(options, this.renderOption)}
+
+ );
+ }
+}
+
+const StyledTabMenu = connectStyle('shoutem.ui.TabMenu')(TabMenu);
+
+export { StyledTabMenu as TabMenu };
diff --git a/components/TabMenu/TabMenuItem.js b/components/TabMenu/TabMenuItem.js
new file mode 100644
index 00000000..55c9adc4
--- /dev/null
+++ b/components/TabMenu/TabMenuItem.js
@@ -0,0 +1,65 @@
+import React, { PureComponent } from 'react';
+import { LayoutAnimation } from 'react-native';
+import PropTypes from 'prop-types';
+import autoBindReact from 'auto-bind/react';
+import _ from 'lodash';
+import { connectStyle } from '@shoutem/theme';
+import { Text } from '../Text';
+import { View } from '../View';
+import { TouchableOpacity } from '../TouchableOpacity';
+import { optionShape } from './const';
+
+class TabMenuItem extends PureComponent {
+ static propTypes = {
+ item: optionShape,
+ onItemPressed: PropTypes.any,
+ isSelected: PropTypes.bool,
+ style: PropTypes.object,
+ };
+
+ constructor(props) {
+ super(props);
+
+ autoBindReact(this);
+
+ this.state = {
+ baseWidth: 0,
+ };
+ }
+
+ handleItemPressed() {
+ const { onItemPressed, item } = this.props;
+
+ onItemPressed(item);
+ }
+
+ handleLayout({ nativeEvent: { layout: { width } } }) {
+
+ LayoutAnimation.easeInEaseOut();
+ this.setState({ baseWidth: width });
+ }
+
+ render() {
+ const { style, isSelected, item } = this.props;
+ const { baseWidth } = this.state;
+
+ return (
+
+
+ {item.title}
+
+ {isSelected && }
+
+ );
+ }
+}
+
+const StyledTabMenuItem = connectStyle('shoutem.ui.TabMenuItem')(TabMenuItem);
+
+export { StyledTabMenuItem as TabMenuItem };
diff --git a/components/TabMenu/const.js b/components/TabMenu/const.js
new file mode 100644
index 00000000..b3c8be74
--- /dev/null
+++ b/components/TabMenu/const.js
@@ -0,0 +1,6 @@
+import PropTypes from 'prop-types';
+
+export const optionShape = PropTypes.shape({
+ title: PropTypes.string,
+ value: PropTypes.any,
+});
diff --git a/components/TabMenu/index.js b/components/TabMenu/index.js
new file mode 100644
index 00000000..2f185a51
--- /dev/null
+++ b/components/TabMenu/index.js
@@ -0,0 +1 @@
+export { TabMenu } from './TabMenu';
diff --git a/components/YearRangePicker/YearRangePicker.js b/components/YearRangePicker/YearRangePicker.js
new file mode 100644
index 00000000..766c5518
--- /dev/null
+++ b/components/YearRangePicker/YearRangePicker.js
@@ -0,0 +1,105 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import _ from 'lodash';
+import autoBindReact from 'auto-bind/react';
+import { connectStyle } from '@shoutem/theme';
+import { View } from '../View';
+import YearPickerButton from './YearRangePickerButton';
+import YearPickerModal from './YearRangePickerModal';
+
+function formatButtonTooltip(props) {
+ const { selectedYears, buttonPlaceholder } = props;
+
+ if (_.isEmpty(selectedYears)) {
+ return buttonPlaceholder;
+ }
+
+ const leadYear = _.head(selectedYears);
+ const lastYear = _.last(selectedYears);
+
+ if (leadYear === lastYear) {
+ return leadYear.toString();
+ }
+
+ return `${leadYear}-${lastYear}`;
+}
+
+class YearRangePicker extends PureComponent {
+ static propTypes = {
+ onRangeConfirmed: PropTypes.func,
+ onReset: PropTypes.func,
+ resetButtonTitle: PropTypes.string,
+ confirmButtonTitle: PropTypes.string,
+ selectedYears: PropTypes.arrayOf(PropTypes.number),
+ rangeStart: PropTypes.number,
+ rangeEnd: PropTypes.number,
+ buttonPlaceholder: PropTypes.string,
+ };
+
+ static defaultProps = {
+ selectedYears: [],
+ buttonPlaceholder: 'Year',
+ };
+
+ constructor(props) {
+ super(props);
+
+ autoBindReact(this);
+
+ this.state = {
+ collapsed: false,
+ selectedYears: props.selectedYears,
+ buttonTooltip: formatButtonTooltip(props),
+ };
+ }
+
+ componentDidUpdate(prevProps) {
+ const { selectedYears: prevSelectedYears } = prevProps;
+ const { selectedYears } = this.props;
+
+ if (!_.isEqual(selectedYears, prevSelectedYears)) {
+ this.setState({ buttonTooltip: formatButtonTooltip(this.props) });
+ }
+ }
+
+ handleButtonPressed() {
+ const { collapsed } = this.state;
+
+ this.setState({ collapsed: !collapsed });
+ }
+
+ handleRangeConfirmed(range) {
+ const { onRangeConfirmed } = this.props;
+
+ this.setState({ collapsed: false });
+
+ if (onRangeConfirmed) {
+ onRangeConfirmed(range);
+ }
+ }
+
+ render() {
+ const { rangeEnd, rangeStart, resetButtonTitle, confirmButtonTitle } = this.props;
+ const { buttonTooltip, collapsed } = this.state;
+
+ return (
+
+
+
+
+ );
+ }
+}
+
+export default connectStyle('shoutem.ui.YearRangePicker')(YearRangePicker);
diff --git a/components/YearRangePicker/YearRangePickerButton.js b/components/YearRangePicker/YearRangePickerButton.js
new file mode 100644
index 00000000..02a67004
--- /dev/null
+++ b/components/YearRangePicker/YearRangePickerButton.js
@@ -0,0 +1,82 @@
+import React, { PureComponent } from 'react';
+import { Animated } from 'react-native';
+import PropTypes from 'prop-types';
+import _ from 'lodash';
+import autoBindReact from 'auto-bind/react';
+import { connectStyle } from '@shoutem/theme';
+import { TouchableOpacity } from '../TouchableOpacity';
+import { Icon } from '../Icon';
+import { Text } from '../Text';
+
+const AnimatedIcon = Animated.createAnimatedComponent(Icon);
+
+class YearRangePickerButton extends PureComponent {
+ static propTypes = {
+ onPress: PropTypes.func,
+ tooltip: PropTypes.string,
+ style: PropTypes.any,
+ };
+
+ constructor(props) {
+ super(props);
+
+ autoBindReact(this);
+
+ this.dropDownIconValue = new Animated.Value(0);
+
+ this.state = {
+ collapsed: false,
+ };
+ }
+
+ handlePress() {
+ const { collapsed } = this.state;
+ const { onPress } = this.props;
+
+ const toValue = collapsed ? 0 : 1;
+
+ this.setState(
+ { collapsed: !collapsed },
+ () => Animated.timing(
+ this.dropDownIconValue,
+ {
+ toValue,
+ useNativeDriver: true,
+ duration: 300,
+ }
+ ).start());
+
+ if (onPress) {
+ onPress();
+ }
+ }
+
+ render() {
+ const { style, tooltip } = this.props;
+
+ return (
+
+
+ {tooltip}
+
+
+
+ );
+ }
+}
+
+export default connectStyle('shoutem.ui.YearRangePickerButton')(YearRangePickerButton);
diff --git a/components/YearRangePicker/YearRangePickerModal.js b/components/YearRangePicker/YearRangePickerModal.js
new file mode 100644
index 00000000..295324e2
--- /dev/null
+++ b/components/YearRangePicker/YearRangePickerModal.js
@@ -0,0 +1,258 @@
+import React, { PureComponent } from 'react';
+import { LayoutAnimation, Platform } from 'react-native';
+import PropTypes from 'prop-types';
+import _ from 'lodash';
+import Modal from 'react-native-modal';
+import autoBindReact from 'auto-bind/react';
+import { connectStyle } from '@shoutem/theme';
+import { View } from '../View';
+import { Text } from '../Text';
+import { Icon } from '../Icon';
+import { Button } from '../Button';
+import { TouchableOpacity } from '../TouchableOpacity';
+
+const NUMBER_OF_ROWS = 4;
+const YEARS_IN_ROW = 5;
+
+function resolveVisibleYears(props) {
+ const { selectedYears, rangeStart, rangeEnd } = props;
+
+ const yearsPerPage = NUMBER_OF_ROWS * YEARS_IN_ROW;
+
+ if (_.isEmpty(selectedYears)) {
+ const fullRange = _.times(
+ yearsPerPage,
+ index => rangeStart + index,
+ );
+
+ return _.filter(fullRange, year => year <= rangeEnd);
+ }
+
+ const firstSelectedYear = _.head(selectedYears);
+ const prevPagesNumber = Math.trunc(firstSelectedYear / yearsPerPage);
+
+ const fullRangeStart = (prevPagesNumber * yearsPerPage) + 1;
+ const fullRange = _.times(
+ yearsPerPage,
+ index => fullRangeStart + index,
+ );
+
+ return _.filter(fullRange, year => year <= rangeEnd);
+}
+
+function resolveRangeTooltip(visibleYears) {
+ const startYear = _.head(visibleYears).toString();
+ const endYear = _.last(visibleYears).toString();
+
+ return `${startYear}-${endYear}`;
+}
+
+class YearRangePickerModal extends PureComponent {
+ static propTypes = {
+ onRangeConfirmed: PropTypes.func,
+ onDismiss: PropTypes.func,
+ resetButtonTitle: PropTypes.string,
+ confirmButtonTitle: PropTypes.string,
+ selectedYears: PropTypes.arrayOf(PropTypes.number),
+ rangeStart: PropTypes.number,
+ rangeEnd: PropTypes.number,
+ visible: PropTypes.bool,
+ style: PropTypes.any,
+ };
+
+ static defaultProps = {
+ selectedYears: [],
+ };
+
+ constructor(props) {
+ super(props);
+
+ autoBindReact(this);
+
+ this.YEARS_PER_PAGE = NUMBER_OF_ROWS * YEARS_IN_ROW;
+
+ this.state = {
+ collapsed: false,
+ visibleYears: resolveVisibleYears(props),
+ selectedYears: props.selectedYears,
+ };
+ }
+
+ handleConfirmPress() {
+ const { selectedYears } = this.state;
+ const { onRangeConfirmed } = this.props;
+
+ if (onRangeConfirmed) {
+ LayoutAnimation.easeInEaseOut();
+ onRangeConfirmed(selectedYears);
+ }
+ }
+
+ handleResetPress() {
+ const { onRangeConfirmed } = this.props;
+
+ LayoutAnimation.easeInEaseOut();
+ this.setState({ selectedYears: [] });
+
+ if (onRangeConfirmed) {
+ onRangeConfirmed([]);
+ }
+ }
+
+ handleYearsForwardPress() {
+ const { rangeEnd } = this.props;
+ const { visibleYears } = this.state;
+
+ const nextYearStart = _.last(visibleYears) + 1;
+ const nextYearEnd = Math.min(rangeEnd, nextYearStart + this.YEARS_PER_PAGE - 1);
+
+ const nextVisibleYears = _.times(nextYearEnd + 1 - nextYearStart, index => nextYearStart + index);
+
+ LayoutAnimation.easeInEaseOut();
+ this.setState({ visibleYears: nextVisibleYears });
+ }
+
+ handleYearsBackPress() {
+ const { visibleYears } = this.state;
+
+ const prevYearStart = _.head(visibleYears) - this.YEARS_PER_PAGE;
+
+ LayoutAnimation.easeInEaseOut();
+ this.setState({ visibleYears: _.times(this.YEARS_PER_PAGE, index => prevYearStart + index) });
+ }
+
+ handleYearPress(year) {
+ const { selectedYears } = this.state;
+
+ const size = _.size(selectedYears);
+ const index = _.indexOf(selectedYears, year);
+
+ LayoutAnimation.easeInEaseOut();
+
+ return () => {
+ if (_.includes(selectedYears, year)) {
+ if (_.last(selectedYears) === year || _.head(selectedYears) === year) {
+ this.setState({ selectedYears: _.without(selectedYears, year) });
+ return;
+ }
+
+ const cutFromEnd = size / 2 <= index;
+ const newYears = _.filter(selectedYears, value => cutFromEnd ? value <= year : value >= year);
+
+ this.setState({ selectedYears: newYears });
+ return;
+ }
+
+ if (_.isEmpty(selectedYears)) {
+ this.setState({ selectedYears: [year] });
+ return;
+ }
+
+ const addToEnd = _.last(selectedYears) < year;
+ const yearsToAdd = addToEnd ? year - _.last(selectedYears) : _.head(selectedYears) - year;
+
+ const newYears = addToEnd
+ ? [...selectedYears, ..._.times(yearsToAdd, index => _.last(selectedYears) + index + 1)]
+ : [...selectedYears, ..._.times(yearsToAdd, index => _.head(selectedYears) - index + -1)]
+ const sortedYears = _.sortBy(newYears, item => item);
+
+ this.setState({ selectedYears: sortedYears });
+ }
+ }
+
+ renderYear(year) {
+ const { style } = this.props;
+ const { selectedYears } = this.state;
+
+ const isSelected = _.includes(selectedYears, year);
+ const isFirst = _.head(selectedYears) === year;
+ const isLast = _.last(selectedYears) === year;
+
+ return (
+
+
+ {year.toString()}
+
+
+ );
+ }
+
+ renderYearRow(row, index) {
+ return (
+
+ {_.map(row, this.renderYear)}
+
+ )
+ }
+
+ render() {
+ const {
+ style,
+ visible,
+ confirmButtonTitle,
+ resetButtonTitle,
+ rangeEnd,
+ rangeStart,
+ onDismiss,
+ } = this.props;
+ const { visibleYears } = this.state;
+
+ const buttonTooltip = resolveRangeTooltip(visibleYears);
+ const data = _.chunk(visibleYears, YEARS_IN_ROW);
+ const nextDisabled = _.last(visibleYears) === rangeEnd;
+ const prevDisabled = _.head(visibleYears) - 1 < rangeStart;
+
+ if (!visible) {
+ return null;
+ }
+
+ return (
+
+
+
+
+ {buttonTooltip}
+
+
+ {_.times(NUMBER_OF_ROWS, (row) => this.renderYearRow(data[row], row))}
+
+
+
+
+ View>
+
+ );
+ }
+}
+
+export default connectStyle('shoutem.ui.YearRangePickerModal')(YearRangePickerModal);
diff --git a/components/YearRangePicker/index.js b/components/YearRangePicker/index.js
new file mode 100644
index 00000000..21382b2d
--- /dev/null
+++ b/components/YearRangePicker/index.js
@@ -0,0 +1 @@
+export { default as YearRangePicker } from './YearRangePicker';
diff --git a/index.js b/index.js
index d96653f5..d2387945 100644
--- a/index.js
+++ b/index.js
@@ -72,10 +72,13 @@ export { Tile } from './components/Tile';
export { Lightbox } from './components/Lightbox';
+export { InlineDropDownMenu } from './components/InlineDropDownMenu';
export { EmptyStateView } from './components/EmptyStateView';
export { EmptyListImage } from './components/EmptyListImage';
export { NumberInput } from './components/NumberInput';
export { SearchField } from './components/SearchField';
+export { TabMenu } from './components/TabMenu';
+export { YearRangePicker } from './components/YearRangePicker';
export { Examples } from './examples/components';
diff --git a/package.json b/package.json
index 25c3403e..6e572947 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@shoutem/ui",
- "version": "4.2.1",
+ "version": "4.3.0",
"description": "Styleable set of components for React Native applications",
"scripts": {
"lint": "eslint .",
@@ -25,6 +25,7 @@
"react-native-lightbox": "shoutem/react-native-lightbox#v0.7.2",
"react-native-linear-gradient": "~2.5.6",
"react-native-photo-view": "shoutem/react-native-photo-view#0ffa1481f6b6cb8663cb291b7db1d6644440584d",
+ "react-native-modal": "11.7.0",
"react-native-render-html": "~4.2.0",
"react-native-status-bar-height": "2.5.0",
"react-native-svg": "^9.13.0",
diff --git a/theme.js b/theme.js
index 24f9f713..cec2dc2d 100644
--- a/theme.js
+++ b/theme.js
@@ -485,6 +485,10 @@ export default (variables = defaultThemeVariables) => ({
opacity: 0.5,
},
+ '.link': {
+ ...variables.links,
+ },
+
backgroundColor: 'transparent',
},
@@ -598,7 +602,7 @@ export default (variables = defaultThemeVariables) => ({
'.medium-avatar': {
width: dimensionRelativeToIphone(145),
height: dimensionRelativeToIphone(145),
- borderRadius: 72.5,
+ borderRadius: dimensionRelativeToIphone(72.5),
borderWidth: 0,
},
@@ -661,7 +665,7 @@ export default (variables = defaultThemeVariables) => ({
},
},
'shoutem.ui.Image': {
- [INCLUDE]: ['commonVariants', 'imageSizes', 'fill-parent'],
+ [INCLUDE]: ['commonVariants', 'imageSizes', 'fill-parent', 'guttersMargin'],
'.placeholder': {
backgroundColor: inverseColorBrightnessForAmount(
@@ -694,7 +698,11 @@ export default (variables = defaultThemeVariables) => ({
},
},
'shoutem.ui.ImageBackground': {
- [INCLUDE]: ['commonVariants', 'imageSizes', 'fill-parent'],
+ [INCLUDE]: ['commonVariants', 'imageSizes', 'fill-parent', 'guttersMargin'],
+
+ '.overflow-hidden': {
+ overflow: 'hidden',
+ },
'.placeholder': {
backgroundColor: inverseColorBrightnessForAmount(
@@ -1937,6 +1945,8 @@ export default (variables = defaultThemeVariables) => ({
borderColor: variables.lineColor,
},
'shoutem.ui.Divider': {
+ [INCLUDE]: ['guttersMargin'],
+
'.line': {
'.small': {
width: 55,
@@ -2040,6 +2050,11 @@ export default (variables = defaultThemeVariables) => ({
withoutBorder: {
borderWidth: 0,
},
+
+ '.small': {
+ paddingVertical: 6,
+ height: 42,
+ },
},
'shoutem.ui.NumberInput': {
@@ -2092,6 +2107,7 @@ export default (variables = defaultThemeVariables) => ({
backgroundColor: '#f0f0f0',
color: '#888888',
flex: 1,
+ minWidth: 330,
fontSize: 15,
height: 30,
paddingVertical: 6,
@@ -2551,6 +2567,17 @@ export default (variables = defaultThemeVariables) => ({
},
},
+ '.relative': {
+ container: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ position: 'relative',
+ bottom: 0,
+ paddingVertical: 16,
+ },
+ },
+
container: {
flexDirection: 'row',
alignItems: 'center',
@@ -2860,4 +2887,146 @@ export default (variables = defaultThemeVariables) => ({
},
},
},
+
+ 'shoutem.ui.InlineDropDownMenu': {
+ container: {
+ paddingTop: 12,
+ paddingHorizontal: variables.mediumGutter,
+ paddingBottom: 4,
+ backgroundColor: variables.paperColor,
+ },
+ icon: {
+ color: variables.text.color,
+ }
+ },
+
+ 'shoutem.ui.InlineDropDownMenuItem': {
+ container: {
+ paddingTop: 10,
+ paddingHorizontal: variables.mediumGutter,
+ paddingBottom: 4,
+ backgroundColor: variables.paperColor,
+ borderTopWidth: 1,
+ borderTopColor: variables.backgroundColor,
+ },
+ },
+
+ 'shoutem.ui.TabMenu': {
+ container: {
+ paddingHorizontal: variables.smallGutter,
+ backgroundColor: variables.backgroundColor,
+ },
+ list: {
+ flexGrow: 0,
+ flexShrink: 0,
+ },
+ },
+
+ 'shoutem.ui.TabMenuItem': {
+ tabulator: {
+ backgroundColor: variables.text.color,
+ height: 1,
+ borderRadius: 1,
+ flexDirection: 'row',
+ marginBottom: 8,
+ marginLeft: 8,
+ },
+ text: {
+ marginTop: 12,
+ marginHorizontal: 8,
+ marginBottom: 12,
+ opacity: 0.3,
+ },
+ selectedText: {
+ marginBottom: 4,
+ opacity: 1,
+ }
+ },
+
+ 'shoutem.ui.YearRangePickerButton': {
+ container: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: '#F9F9F9',
+ borderWidth: 1,
+ borderRadius: 8,
+ borderColor: 'rgba(130, 130, 130, 0.1)',
+ paddingLeft: 15,
+ paddingVertical: 8,
+ paddingRight: 8,
+ marginRight: 8,
+ },
+ icon: {
+ color: variables.text.color,
+ }
+ },
+
+ 'shoutem.ui.YearRangePickerModal': {
+ outerContainer: {
+ flex: 1,
+ },
+ container: {
+ height: 360,
+ padding: 8,
+ backgroundColor: '#F9F9F9',
+ borderWidth: 1,
+ borderRadius: 8,
+ borderColor: 'rgba(130, 130, 130, 0.1)',
+ marginHorizontal: 15,
+ },
+ tooltipContainer: {
+ flexDirection: 'row',
+ flex: 1,
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: 8,
+ height: 56,
+ },
+ yearRow: {
+ flexDirection: 'row',
+ flex: 1,
+ },
+ buttonContainer: {
+ flexDirection: 'row',
+ padding: 8,
+ height: 56,
+ },
+ yearContainer: {
+ flex: 1,
+ alignSelf: 'stretch',
+ flexDirection: 'row',
+ marginBottom: 5,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ year: {
+ position: 'absolute',
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ yearSelected: {
+ backgroundColor: variables.featuredColor,
+ },
+ yearFirst: {
+ left: 5,
+ borderTopLeftRadius: 8,
+ borderBottomLeftRadius: 8,
+ },
+ yearLast: {
+ right: 5,
+ borderTopRightRadius: 8,
+ borderBottomRightRadius: 8,
+ },
+ icon: {
+ color: variables.text.color,
+ opacity: 1,
+ },
+ iconDisabled: {
+ opacity: 0.3,
+ }
+ },
});