diff --git a/app/components/Base/HorizontalSelector/index.js b/app/components/Base/HorizontalSelector/index.js
new file mode 100644
index 00000000000..079a400d545
--- /dev/null
+++ b/app/components/Base/HorizontalSelector/index.js
@@ -0,0 +1,271 @@
+import React, { Fragment, useCallback, useMemo } from 'react';
+import PropTypes from 'prop-types';
+import { View, StyleSheet, TouchableOpacity } from 'react-native';
+import Text from '../Text';
+import { colors } from '../../../styles/common';
+
+const INNER_CIRCLE_SCALE = 0.445;
+const OPTION_WIDTH = 110;
+const styles = StyleSheet.create({
+ selector: {
+ display: 'flex',
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ alignItems: 'center'
+ },
+ labels: {
+ display: 'flex',
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ alignItems: 'flex-start'
+ },
+ option: {
+ width: OPTION_WIDTH,
+ display: 'flex',
+ alignItems: 'center',
+ flex: 0,
+ flexDirection: 'column'
+ },
+ circle: size => ({
+ width: size,
+ height: size,
+ flexShrink: 0,
+ flexGrow: 0,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderWidth: 2,
+ borderRadius: 9999,
+ borderColor: colors.grey200
+ }),
+ circleSelected: {
+ borderColor: colors.blue
+ },
+ circleError: {
+ borderColor: colors.red
+ },
+ circleDisabled: {
+ opacity: 0.4
+ },
+ innerCircle: size => ({
+ width: size * INNER_CIRCLE_SCALE,
+ height: size * INNER_CIRCLE_SCALE,
+ flexShrink: 0,
+ flexGrow: 0,
+ backgroundColor: colors.blue,
+ borderRadius: 999
+ }),
+ innerCircleError: {
+ backgroundColor: colors.red
+ },
+ verticalLine: {
+ marginTop: 2,
+ marginBottom: -1,
+ width: 0,
+ height: 4,
+ borderLeftWidth: 1,
+ borderColor: colors.grey200
+ },
+ topVerticalLine: {
+ marginTop: 0,
+ marginBottom: 2,
+ width: 0,
+ height: 4,
+ borderLeftWidth: 1,
+ borderColor: colors.blue
+ },
+ line: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ justifyContent: 'space-around',
+ width: '100%',
+ marginBottom: 2
+ },
+ lineHolder: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ borderWidth: 0
+ },
+ lineFill: {
+ flex: 1
+ },
+ lineVisible: {
+ borderTopWidth: 1,
+ borderColor: colors.grey200
+ },
+ circleHitSlop: {
+ top: 0,
+ bottom: 20,
+ left: 0,
+ right: 0
+ }
+});
+
+function Circle({ size = 22, selected, disabled, error }) {
+ return (
+
+ {selected && (
+
+ )}
+
+ );
+}
+Circle.propTypes = {
+ size: PropTypes.number,
+ selected: PropTypes.bool,
+ disabled: PropTypes.bool,
+ error: PropTypes.bool
+};
+
+function Option({ onPress, name, ...props }) {
+ const handlePress = useCallback(() => onPress(name), [name, onPress]);
+ return ;
+}
+
+Option.propTypes = {
+ onPress: PropTypes.func,
+ name: PropTypes.string
+};
+
+function HorizontalSelector({ options = [], selected, circleSize, onPress, disabled, ...props }) {
+ const hasTopLabels = useMemo(() => options.some(option => option.topLabel), [options]);
+ return (
+
+ {hasTopLabels && (
+
+ {options.map(option =>
+ option.topLabel ? (
+
+ {typeof option.topLabel === 'string' ? (
+
+ {option.topLabel}
+
+ ) : typeof option.topLabel === 'function' ? (
+ option.topLabel(option.name === selected, option.disabled ?? disabled)
+ ) : (
+ option.topLabel
+ )}
+
+
+ ) : (
+
+ )
+ )}
+
+ )}
+
+ {options.map(option => (
+
+ ))}
+
+
+ {options.map((option, index, array) => (
+
+
+
+
+
+
+
+
+ ))}
+
+
+ {options.map(option => (
+
+ ))}
+
+
+ );
+}
+
+HorizontalSelector.propTypes = {
+ /**
+ * Array of options
+ */
+ options: PropTypes.arrayOf(
+ PropTypes.shape({
+ /**
+ * Label of the option. It can be a string, component or a render
+ * function, which will be called with arguments (selected, disabled).
+ */
+ label: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
+ /**
+ * Top label of the option. It can be a string, component or a render function.
+ */
+ topLabel: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
+ /**
+ * Option name string, this is used as argument when calling the onPress function.
+ */
+ name: PropTypes.string,
+ /**
+ * Boolean value to determine whether if option is disabled or not.
+ */
+ disabled: PropTypes.bool,
+ /**
+ * Boolean value to determine if the option should represent an error
+ */
+ error: PropTypes.bool
+ })
+ ),
+ /**
+ * Boolean value to determine whether the options are disabked or not.
+ */
+ disabled: PropTypes.bool,
+ /**
+ * Function that is called when pressing an option. The function is called with option.name argument.
+ */
+ onPress: PropTypes.func,
+ /**
+ * Size of the option circle
+ */
+ circleSize: PropTypes.number,
+ /**
+ * Current option name selected
+ */
+ selected: PropTypes.string
+};
+
+export default HorizontalSelector;
diff --git a/app/components/UI/EditGasFee1559/index.js b/app/components/UI/EditGasFee1559/index.js
index 121d5afd1af..31b41bbd0c4 100644
--- a/app/components/UI/EditGasFee1559/index.js
+++ b/app/components/UI/EditGasFee1559/index.js
@@ -1,3 +1,4 @@
+/* eslint-disable react/display-name */
import React, { useCallback, useState } from 'react';
import { View, StyleSheet, TouchableOpacity } from 'react-native';
import Text from '../../Base/Text';
@@ -7,6 +8,7 @@ import { colors } from '../../../styles/common';
import InfoModal from '../Swaps/components/InfoModal';
import Icon from 'react-native-vector-icons/Ionicons';
import { strings } from '../../../../locales/i18n';
+import HorizontalSelector from '../../Base/HorizontalSelector';
const styles = StyleSheet.create({
root: {
@@ -50,6 +52,7 @@ const EditGasFee1559 = () => {
const [showRangeInfoModal, setShowRangeInfoModal] = useState(false);
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
const [maxPriorityFeeError, setMaxPriorityFeeError] = useState(null);
+ const [selectedOption, setSelectedOption] = useState(null);
const toggleRangeInfoModal = useCallback(() => {
setShowRangeInfoModal(showRangeInfoModal => !showRangeInfoModal);
@@ -73,6 +76,44 @@ const EditGasFee1559 = () => {
SELECTOR
+ {/* TODO: hook with controller, add strings i18n */}
+ Lower
+ },
+ {
+ name: 'medium',
+ label: (selected, disabled) => (
+
+ Medium
+
+ )
+ },
+
+ {
+ name: 'high',
+ error: true,
+ label: (selected, disabled) => (
+
+ Higher
+
+ ),
+ topLabel: (
+
+
+ Recommended{' '}
+
+
+
+ )
+ }
+ ]}
+ />