Untitled

mail@pastecode.io avatar
unknown
plain_text
a month ago
6.0 kB
3
Indexable
Never
import React, { useEffect, useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, Modal, ScrollView } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import tailwind from 'tailwind-rn';

const tagActivityMap = {
  "running": ["Tempo", "Recovery", "Hills", "Speed", "Long Run"],
  "cycling": ["Hills", "Recovery", "Endurance", "Interval", "Threshold"],
  "other": ["Strength", "HIIT", "Studio", "Cardio"],
};

const filterTags = (arr1, arr2) => {
  const lowerCaseArr2 = arr2.map(value => value.toLowerCase());
  return arr1.filter(value => !lowerCaseArr2.includes(value.toLowerCase()));
};

const processTags = (defaultTags, customTags, inputTags) => {
  const uniqueTags = [...new Set([...defaultTags, ...customTags])];
  return filterTags(uniqueTags, inputTags);
};

const TagSelector = ({ activityType = "running", inputTags = [], onSelect, canEdit = false, customTags = [] }) => {
  const [showModal, setShowModal] = useState(false);
  const [searchInput, setSearchInput] = useState('');
  const [options, setOptions] = useState(processTags(tagActivityMap[activityType], customTags, inputTags));
  const [filteredOptions, setFilteredOptions] = useState(options);
  const [outputTags, setOutputTags] = useState(inputTags);

  useEffect(() => {
    if (activityType) {
      const fOpt = processTags(tagActivityMap[activityType], customTags, inputTags);
      setOptions(fOpt);
      setFilteredOptions(fOpt);
    }
  }, [activityType]);

  useEffect(() => {
    if (inputTags) {
      setOutputTags(inputTags);
      if (activityType) {
        const fOpt = processTags(tagActivityMap[activityType], customTags, inputTags);
        setOptions(fOpt);
        setFilteredOptions(fOpt);
      }
    }
  }, [JSON.stringify(inputTags), JSON.stringify(customTags)]);

  const handleSearchInputChange = (input) => {
    setSearchInput(input);
    const filtered = options.filter(option => option.toLowerCase().includes(input.toLowerCase()));
    setFilteredOptions(filtered);
  };

  const addTag = (tag) => {
    let newOutputTags = [...outputTags];
    if (!outputTags.some(t => t.toLowerCase() === tag.toLowerCase())) {
      newOutputTags.push(tag);
    }
    setOutputTags(newOutputTags);
    const fOpts = processTags(tagActivityMap[activityType], customTags, newOutputTags);
    setOptions(fOpts);
    setFilteredOptions(fOpts);
    onSelect(newOutputTags);
    setShowModal(false);
    setSearchInput('');
  };

  const removeTag = (tag) => {
    const newOutputTags = outputTags.filter(t => t.toLowerCase() !== tag.toLowerCase());
    setOutputTags(newOutputTags);
    const fOpts = processTags(tagActivityMap[activityType], customTags, newOutputTags);
    setOptions(fOpts);
    setFilteredOptions(fOpts);
    onSelect(newOutputTags);
  };

  const cancelModal = () => {
    setShowModal(false);
    setSearchInput('');
  };

  return (
    <View className="flex-1 items-center justify-center">
      <View className="w-full flex-row justify-between items-center">
        <Text className="text-lg font-light text-gray-700 pr-4">Tags</Text>
        <TouchableOpacity 
          className={`flex-1 ${canEdit && outputTags.length < 5 ? 'border border-green-400' : 'border border-gray-300'} p-2 rounded`} 
          onPress={() => canEdit && outputTags.length < 5 ? setShowModal(true) : null}
        >
          <View className="flex-row items-center justify-between">
            <Text className="text-gray-600">{outputTags.length > 0 ? outputTags.join(', ') : 'Select'}</Text>
            <Ionicons name="chevron-down" size={20} color="gray" />
          </View>
        </TouchableOpacity>
      </View>

      {outputTags.length > 0 && (
        <View className="flex-row flex-wrap justify-end w-full pt-2">
          {outputTags.map((tag, idx) => (
            <TouchableOpacity 
              key={idx} 
              className={`bg-gray-200 px-2 py-1 rounded-full mr-2 mb-2 flex-row items-center ${canEdit ? '' : 'opacity-50'}`}
              onPress={() => canEdit ? removeTag(tag) : null}
            >
              <Text className="text-sm">{tag}</Text>
              {canEdit && <Ionicons name="close" size={12} color="gray" className="ml-1" />}
            </TouchableOpacity>
          ))}
        </View>
      )}

      <Modal visible={showModal} transparent={true} animationType="slide">
        <View className="flex-1 justify-center items-center bg-white bg-opacity-85">
          <View className="w-11/12 bg-white rounded-lg p-6 shadow-lg">
            <TextInput
              placeholder="Search or add new tag"
              value={searchInput}
              onChangeText={handleSearchInputChange}
              className="border-b border-gray-300 mb-6 pb-2"
              maxLength={20}
            />
            <ScrollView className="max-h-40">
              {filteredOptions.map((option, idx) => (
                <TouchableOpacity key={idx} className="py-2 border-b border-gray-300" onPress={() => addTag(option)}>
                  <Text>{option}</Text>
                </TouchableOpacity>
              ))}
              {filteredOptions.length === 0 && (
                <TouchableOpacity 
                  className={`flex-row items-center py-2 ${searchInput ? '' : 'opacity-50'}`} 
                  onPress={() => searchInput ? addTag(searchInput) : null}
                >
                  <Ionicons name="add" size={20} color="gray" className="mr-2" />
                  <Text>Create New Tag "{searchInput}"</Text>
                </TouchableOpacity>
              )}
            </ScrollView>
            <TouchableOpacity className="mt-6 bg-gray-700 py-3 rounded-lg" onPress={cancelModal}>
              <Text className="text-center text-white text-lg">Cancel</Text>
            </TouchableOpacity>
          </View>
        </View>
      </Modal>
    </View>
  );
};

export default TagSelector;
Leave a Comment