Реагировать на собственный заводской шаблон - PullRequest
0 голосов
/ 20 июня 2020

У меня в приложении два типа ввода текста. Поисковый ввод и другие текстовые вводы (для форм, комментариев ...), которые могут иметь значок.

Поисковый ввод получает onSubmit (для клавиатуры), который также используется в сенсорной непрозрачности его значок. Другие текстовые поля ввода не имеют свойства onSubmit, но их значки могут иметь функцию onPress.

Я никогда не реализовывал фабричный шаблон в React, могу ли я его использовать? Если нет, следует ли обобщить компонент ввода текста? Или два компонента - это нормально?

Что вы думаете?

Поисковый ввод:

import React, { useState } from "react";
import { View, TouchableOpacity, Keyboard, StyleSheet } from "react-native";
import { TextInput, useTheme } from "react-native-paper";
import { Icon } from "react-native-elements";
import PropTypes from "prop-types";

export default function SearchInput(props) {
  const { colors } = useTheme();

  let { placeholder, maxLength, color, onSearch } = props;

  const [text, setText] = useState("");

  if (!color) {
    color = colors.white;
  }

  const handleChangeText = (text) => {
    setText(text);
  };

  const handleSubmit = () => {
    onSearch(text);
    // Close the keyboard
    Keyboard.dismiss();
  };

  return (
    <>
      <View style={styles.inputContainer}>
        <TextInput
          placeholder={placeholder}
          value={text}
          maxLength={maxLength}
          dense
          autoCorrect={false}
          autoCapitalize="none"
          selectionColor={colors.primary}
          onChangeText={handleChangeText}
          onSubmitEditing={handleSubmit}
          style={[styles.input, { backgroundColor: color }]}
        />
        <View pointerEvents="box-none" style={styles.iconContainer}>
          <TouchableOpacity activeOpacity={0.5} onPress={handleSubmit}>
            <Icon name="search" type="material" color={colors.gray} size={20} />
          </TouchableOpacity>
        </View>
      </View>
    </>
  );
}

SearchInput.propTypes = {
  placeholder: PropTypes.string,
  maxLength: PropTypes.number,
  color: PropTypes.string,
  onSearch: PropTypes.func.isRequired,
};

SearchInput.defaultProps = {
  maxLength: 30,
};

const styles = StyleSheet.create({
  inputContainer: {
    width: "100%",
  },
  input: {
    paddingRight: 5,
    fontSize: 14,
    padding: 5,
  },
  iconContainer: {
    ...StyleSheet.absoluteFillObject,
    justifyContent: "center",
    alignItems: "flex-end",
    marginRight: 5,
  },
});

Другой текстовый ввод:

import React, { useState, forwardRef } from "react";
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
import { TextInput as RNPTextInput, useTheme } from "react-native-paper";
import { Icon } from "react-native-elements";
import PropTypes from "prop-types";

const TextInput = forwardRef((props, ref) => {
  const { colors } = useTheme();

  let {
    placeholder,
    multiline,
    maxLength,
    color,
    icon,
    counter,
    onSubmit,
  } = props;

  const [text, setText] = useState("");

  if (!color) {
    color = colors.white;
  }

  // Default props for optional nested object
  if (icon) {
    if (!icon.color) {
      icon.color = colors.gray;
    }

    if (!icon.size) {
      icon.size = 20;
    }

    if (!icon.onPress) {
      icon.onPress = () => {};
    }
  }

  const handleChangeText = (text) => {
    setText(text);
  };

  const handleIconPress = () => {
    const { onPress } = icon;
    onPress();
  };

  const handleSubmit = () => {
    onSubmit(text);
  };

  return (
    <>
      <View style={styles.inputContainer}>
        <RNPTextInput
          ref={ref}
          placeholder={placeholder}
          value={text}
          multiline={multiline}
          maxLength={maxLength}
          maxHeight={85}
          dense
          autoCorrect={false}
          autoCapitalize="none"
          selectionColor={colors.primary}
          onChangeText={handleChangeText}
          style={[styles.input, { backgroundColor: color }]}
          onSubmitEditing={handleSubmit}
        />
        {icon && (
          <View pointerEvents="box-none" style={styles.iconContainer}>
            <TouchableOpacity activeOpacity={0.5} onPress={handleIconPress}>
              <Icon
                name={icon.name}
                type={icon.type}
                color={icon.color}
                size={icon.size}
              />
            </TouchableOpacity>
          </View>
        )}
      </View>
      {counter && (
        <View style={styles.charactersCounterContainer}>
          <Text style={[styles.charactersCounter, { color: colors.gray }]}>
            {`${text.length}/${maxLength}`}
          </Text>
        </View>
      )}
    </>
  );
});

TextInput.propTypes = {
  placeholder: PropTypes.string,
  multiline: PropTypes.bool,
  maxLength: PropTypes.number,
  color: PropTypes.string,
  icon: PropTypes.shape({
    name: PropTypes.string.isRequired,
    type: PropTypes.string.isRequired,
    color: PropTypes.string,
    size: PropTypes.number,
    onPress: PropTypes.func,
  }),
  counter: PropTypes.bool,
  onSubmit: PropTypes.func,
};

TextInput.defaultProps = {
  placeholder: null,
  multiline: false,
  maxLength: 300,
  icon: null,
  counter: false,
  onSubmit: () => {},
};

export default TextInput;

const styles = StyleSheet.create({
  inputContainer: {
    width: "100%",
  },
  input: {
    paddingRight: 5,
    fontSize: 14,
    padding: 5,
  },
  iconContainer: {
    ...StyleSheet.absoluteFillObject,
    justifyContent: "center",
    alignItems: "flex-end",
    marginRight: 5,
  },
  charactersCounterContainer: {
    marginTop: 5,
    width: "100%",
  },
  charactersCounter: {
    textAlign: "right",
    fontSize: 12,
  },
});
...