<commit_before>import React, { Component } from "react";
import { Creatable }  from 'react-select';
import {connect} from 'react-redux'
import request from "superagent/lib/client";
import { translate } from "react-i18next";

import { OFDB_API }  from "../constants/URLs"

import normalize from "../util/normalize";

class SelectTags extends Component {

  constructor(props) {
    super(props);

    this.state = {
      allOptions: [],
      options: []
    };

    //TODO: List of Tags should probably be loaded with the WebAPI or use Async react-select

    request
      .get( OFDB_API.link +'/tags/')
      .accept('json')
      .end((err, response) => {
        if (err) {
          console.error(err);
        }
        if (response.body) {
          let options = []
          for (var i = 0; i < response.body.length; i++) {
            options[i] = {
              "value": response.body[i],
              "label": response.body[i]
            }
          }
          this.setState({
            allOptions: options
          })
        }
      });
  }

  onInputChange(input) {
    this.filterOptions(input)
  }

  filterOptions(input) {
    let res
    if(input.length < 2){
      res = []
    }
    else {
      let searchString = input.toLowerCase().trim();
      res = this.state.allOptions.filter(function(d) {
        return d.label.match( searchString );
      });
    }

    res = res.slice(0, 30)
    this.setState({
      options: res
    })

  }

  valueToArray() {
    const value = this.props.input.value

    if(!value) return null
    if( typeof value !== "string") return value

    return value.split(',').map( val => {
      return {value: val, label: val }
    })
  }

  valueToString(cb,newValue,event) {

    const val = newValue  // ? newValue : this.props.input.value
    if( typeof val === "string" ) return val

    const currentTagsArray = this.props.input.value.split(',')


    let arr = []
    for (let i = 0; i < val.length; i++) {
      const normalized = normalize.tags(val[i].value)
      if ( normalized==false ) continue

      const isNew = (i == (val.length -1) && event.action == "create-option")
      if (isNew ) if (currentTagsArray.indexOf(normalized) != -1 ) return false

      arr.push( normalized )
    }
    cb( arr.join(',') );
  }

  validate(input) {
    return (input.length > 2 )
  }

  render(){

    return(
      <Creatable
        {...this.props}

        isClearable={false}
        isMulti={true}

        options={this.state.options || []}
        placeholder={this.props.t("entryForm.tags")}
        noOptionsMessage={() => this.props.t("entryForm.noTagSuggestion") }
        formatCreateLabel={(inputValue) => this.props.t("entryForm.newTag")+" "+normalize.tags(inputValue) }

        onInputChange={this.onInputChange.bind(this)}
        onChange={ (value,event) => {this.valueToString( this.props.input.onChange, value, event ) } }
        onBlur={event => event.preventDefault()}
        value={ this.valueToArray() }
        isValidNewOption = { this.validate }
      />
    )
  }
}


const mapStateToProps = ({search}, ownProps) => {
  const tokens = search.history.split(' ').filter(t => t.length)
  const tags = tokens.filter(t => t.startsWith('#')).map(t => {
    // remove all '#' signs from the beginning
    while(t.charAt(0) === '#') {
      t = t.substr(1)
    }

    return t
  })
    .filter(t => t.length)

  return {
    ...ownProps,
    input: {
      ...ownProps.input,
      value: tags.join()
    }
  }
}

module.exports = translate('translation')(connect(mapStateToProps, null)(SelectTags))<commit_msg>feat: pass tags from search field to "add a new entry"<commit_after>import React, { Component } from "react";
import update from "immutability-helper";
import { Creatable }  from 'react-select';
import {connect} from 'react-redux'
import request from "superagent/lib/client";
import { translate } from "react-i18next";

import { OFDB_API }  from "../constants/URLs"

import normalize from "../util/normalize";

class SelectTags extends Component {

  constructor(props) {
    super(props);

    this.state = {
      allOptions: [],
      options: [],
      tagsFromSearch: null
    };

    //TODO: List of Tags should probably be loaded with the WebAPI or use Async react-select

    request
      .get( OFDB_API.link +'/tags/')
      .accept('json')
      .end((err, response) => {
        if (err) {
          console.error(err);
        }
        if (response.body) {
          let options = []
          for (var i = 0; i < response.body.length; i++) {
            options[i] = {
              "value": response.body[i],
              "label": response.body[i]
            }
          }
          this.setState({
            allOptions: options
          })
        }
      });
  }

  componentDidMount() {
    this.setState({tagsFromSearch: this.props.tagsFromSearch})
  }

  onInputChange(input) {
    this.filterOptions(input)
  }

  filterOptions(input) {
    let res
    if(input.length < 2){
      res = []
    }
    else {
      let searchString = input.toLowerCase().trim();
      res = this.state.allOptions.filter(function(d) {
        return d.label.match( searchString );
      });
    }

    res = res.slice(0, 30)
    this.setState({
      options: res
    })

  }

  valueToArray() {
    const value = this.props.input.value
    const {tagsFromSearch} = this.state

    const createOptions = (input) => (input.split(',').map(val => ({label: val, value: val})))

    if(!value) {
      if(!tagsFromSearch) {
        return null
      }
      return createOptions(tagsFromSearch)
    }

    if( typeof value !== "string") {
      if(!tagsFromSearch) {
        return value
      }
      createOptions(tagsFromSearch).concat(value)
    }

    if(!tagsFromSearch) {
      return createOptions(value)
    }

    return createOptions(value)
  }

  valueToString(cb,newValue,event) {
    const val = newValue  // ? newValue : this.props.input.value
    if( typeof val === "string" ) return val

    const {tagsFromSearch} = this.state
    const tagsFromSearchArray = tagsFromSearch ? tagsFromSearch.split(',') : []

    let tagsString = this.props.input.value
    if (tagsFromSearch) {
      if (tagsString.length) {
        tagsString = [tagsFromSearch, tagsString].join()
      }
      else {
        tagsString = tagsFromSearch
      }
    }

    const currentTagsArray = tagsString.split(',')

    const newTagsFromSearchArray = []
    let arr = []
    for (let i = 0; i < val.length; i++) {
      const normalized = normalize.tags(val[i].value)
      if ( normalized===false ) continue

      const isNew = (i === (val.length -1) && event.action === "create-option")
      if (isNew) {
        if (currentTagsArray.indexOf(normalized) !== -1 ) {
          return false
        }
      }

      const tagsFromSearchArrayIdx = tagsFromSearchArray.indexOf(normalized)
      if (event.action === "remove-value" &&  tagsFromSearchArrayIdx !== -1) {
        newTagsFromSearchArray.push(normalized)

        continue
      }

      arr.push( normalized )
    }

    this.setState((prevState) => {
      return update(prevState, {
        tagsFromSearch: {$set: newTagsFromSearchArray.length ? newTagsFromSearchArray.join() : null}
      })
    })

    cb( arr.join(',') );
  }

  validate(input) {
    return (input.length > 2 )
  }

  render(){
    return(
      <Creatable
        {...this.props}

        isClearable={false}
        isMulti={true}

        options={this.state.options || []}
        placeholder={this.props.t("entryForm.tags")}
        noOptionsMessage={() => this.props.t("entryForm.noTagSuggestion") }
        formatCreateLabel={(inputValue) => this.props.t("entryForm.newTag")+" "+normalize.tags(inputValue) }
        onInputChange={this.onInputChange.bind(this)}
        onChange={ (value,event) => {this.valueToString( this.props.input.onChange, value, event ) } }
        onBlur={event => event.preventDefault()}
        value={ this.valueToArray() }
        isValidNewOption = { this.validate }
      />
    )
  }
}


const mapStateToProps = ({search}) => {
  // if history is null
  if(!search.history) {
    return {
      tagsFromSearch: null
    }
  }

  const tokens = search.history.split(' ').filter(t => t.length)
  const tags = tokens.filter(t => t.startsWith('#')).map(t => {
    // remove all '#' signs from the beginning
    while(t.charAt(0) === '#') {
      t = t.substr(1)
    }

    return t
  })
    .filter(t => normalize.tags(t))
    .map(t => normalize.tags(t))

  return {
    tagsFromSearch: tags.join()
  }
}

module.exports = translate('translation')(connect(mapStateToProps, null)(SelectTags))
{'fraction_non_alphanumeric': 0.14923130273956767, 'fraction_numerical': 0.0031210264709282164, 'mean_word_length': 2.3341040462427745, 'pattern_counts': {'":': 4, '<': 11, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1529226', 'n_tokens_mistral': 2735, 'n_tokens_neox': 2673, 'n_words': 555}