<commit_before>/**
 * Return all gene-disease record objects flattened in an array,
 * including annotations, evidence, scores, classifications, variants
 * @param {object} gdm - The gene-disease record data object
 */
export function getAllGdmObjects(gdm) {
    let totalObjects = [];
    // loop through gdms
    let annotations = gdm.annotations && gdm.annotations.length ? gdm.annotations : [];
    annotations.forEach(annotation => {
        // Get annotation records
        totalObjects.push(filteredObject(annotation));
        // loop through groups
        let groups = annotation.groups && annotation.groups.length ? annotation.groups : [];
        if (groups.length) {
            groups.forEach(group => {
                // Get group evidence
                totalObjects.push(filteredObject(group));
                // loop through families within each group
                let groupFamiliesIncluded = group.familyIncluded && group.familyIncluded.length ? group.familyIncluded : [];
                if (groupFamiliesIncluded.length) {
                    groupFamiliesIncluded.forEach(family => {
                        // Get group's family evidence
                        totalObjects.push(filteredObject(family));
                        // loop through individuals within each family of the group
                        let groupFamilyIndividualsIncluded = family.individualIncluded && family.individualIncluded.length ? family.individualIncluded : [];
                        if (groupFamilyIndividualsIncluded.length) {
                            groupFamilyIndividualsIncluded.forEach(individual => {
                                // Get group's family's individual evidence
                                totalObjects.push(filteredObject(individual));
                                // loop through group's family's individual scores
                                let groupFamilyIndividualScores = individual.scores && individual.scores.length ? individual.scores : [];
                                if (groupFamilyIndividualScores.length) {
                                    groupFamilyIndividualScores.forEach(score => {
                                        // Get scores
                                        totalObjects.push(filteredObject(score));
                                    });
                                }
                            });
                        }
                    });
                }
                // loop through individuals of group
                let groupIndividualsIncluded = group.individualIncluded && group.individualIncluded.length ? group.individualIncluded : [];
                if (groupIndividualsIncluded.length) {
                    groupIndividualsIncluded.forEach(individual => {
                        // Get group's individual evidence
                        totalObjects.push(filteredObject(individual));
                        // loop through group's individual scores
                        let groupIndividualScores = individual.scores && individual.scores.length ? individual.scores : [];
                        if (groupIndividualScores.length) {
                            groupIndividualScores.forEach(score => {
                                // Get scores
                                totalObjects.push(filteredObject(score));
                            });
                        }
                    });
                }
            });
        }

        // loop through families
        let families = annotation.families && annotation.families.length ? annotation.families : [];
        if (families.length) {
            families.forEach(family => {
                // Get family evidence
                totalObjects.push(filteredObject(family));
                // loop through individuals with each family
                let familyIndividualsIncluded = family.individualIncluded && family.individualIncluded.length ? family.individualIncluded : [];
                if (familyIndividualsIncluded.length) {
                    familyIndividualsIncluded.forEach(individual => {
                        // Get family's individual evidence
                        totalObjects.push(filteredObject(individual));
                        // loop through family's individual scores
                        let familyIndividualScores = individual.scores && individual.scores.length ? individual.scores : [];
                        if (familyIndividualScores.length) {
                            familyIndividualScores.forEach(score => {
                                // Get scores
                                totalObjects.push(filteredObject(score));
                            });
                        }
                    });
                }
            });
        }

        // loop through individuals
        let individuals = annotation.individuals && annotation.individuals.length ? annotation.individuals : [];
        if (individuals.length) {
            individuals.forEach(individual => {
                // Get individual evidence
                totalObjects.push(filteredObject(individual));
                // loop through individual scores
                let individualScores = individual.scores && individual.scores.length ? individual.scores : [];
                if (individualScores.length) {
                    individualScores.forEach(score => {
                        // Get scores
                        totalObjects.push(filteredObject(score));
                    });
                }
            });
        }

        // loop through experimentals
        let experimentals = annotation.experimentalData && annotation.experimentalData.length ? annotation.experimentalData : [];
        if (experimentals.length) {
            experimentals.forEach(experimental => {
                // Get individual evidence
                totalObjects.push(filteredObject(experimental));
                // loop through experimental scores
                let experimentalScores = experimental.scores && experimental.scores.length ? experimental.scores : [];
                if (experimentalScores.length) {
                    experimentalScores.forEach(score => {
                        // Get scores
                        totalObjects.push(filteredObject(score));
                    });
                }
            });
        }

        // loop through case-controls
        let caseControls = annotation.caseControlStudies && annotation.caseControlStudies.length ? annotation.caseControlStudies : [];
        if (caseControls.length) {
            caseControls.forEach(caseControl => {
                // Get case-control evidence
                totalObjects.push(filteredObject(caseControl));
                // loop through case-control scores
                let caseControlScores = caseControl.scores && caseControl.scores.length ? caseControl.scores : [];
                if (caseControlScores.length) {
                    caseControlScores.forEach(score => {
                        // Get scores
                        totalObjects.push(filteredObject(score));
                    });
                }
            });
        }
    });
    // Get provisionalClassifications objects
    let classifications = gdm.provisionalClassifications && gdm.provisionalClassifications.length ? gdm.provisionalClassifications : [];
    classifications.forEach(classification => {
        totalObjects.push(filteredObject(classification));
    });
    // Get provisionalClassifications objects
    let variantPathogenicity = gdm.variantPathogenicity && gdm.variantPathogenicity.length ? gdm.variantPathogenicity : [];
    variantPathogenicity.forEach(variant => {
        totalObjects.push(filteredObject(variant));
    });

    return totalObjects;
}

/**
 * Method to filter object keys
 * @param {object} target - A targeted data object in GDM
 */
function filteredObject(target) {
    const allowed = ['date_created', 'last_modified', 'submitted_by', '@type', 'affiliation', '@id'];

    const filtered = Object.keys(target)
        .filter(key => allowed.includes(key))
        .reduce((obj, key) => {
            obj[key] = target[key];
            return obj;
        }, {});

    return filtered;
}<commit_msg>Included Case Cohort and Control Cohort group in Case Control evidence.<commit_after>/**
 * Return all gene-disease record objects flattened in an array,
 * including annotations, evidence, scores, classifications, variants
 * @param {object} gdm - The gene-disease record data object
 */
export function getAllGdmObjects(gdm) {
    let totalObjects = [];
    // loop through gdms
    let annotations = gdm.annotations && gdm.annotations.length ? gdm.annotations : [];
    annotations.forEach(annotation => {
        // Get annotation records
        totalObjects.push(filteredObject(annotation));
        // loop through groups
        let groups = annotation.groups && annotation.groups.length ? annotation.groups : [];
        if (groups.length) {
            groups.forEach(group => {
                // Get group evidence
                totalObjects.push(filteredObject(group));
                // loop through families within each group
                let groupFamiliesIncluded = group.familyIncluded && group.familyIncluded.length ? group.familyIncluded : [];
                if (groupFamiliesIncluded.length) {
                    groupFamiliesIncluded.forEach(family => {
                        // Get group's family evidence
                        totalObjects.push(filteredObject(family));
                        // loop through individuals within each family of the group
                        let groupFamilyIndividualsIncluded = family.individualIncluded && family.individualIncluded.length ? family.individualIncluded : [];
                        if (groupFamilyIndividualsIncluded.length) {
                            groupFamilyIndividualsIncluded.forEach(individual => {
                                // Get group's family's individual evidence
                                totalObjects.push(filteredObject(individual));
                                // loop through group's family's individual scores
                                let groupFamilyIndividualScores = individual.scores && individual.scores.length ? individual.scores : [];
                                if (groupFamilyIndividualScores.length) {
                                    groupFamilyIndividualScores.forEach(score => {
                                        // Get scores
                                        totalObjects.push(filteredObject(score));
                                    });
                                }
                            });
                        }
                    });
                }
                // loop through individuals of group
                let groupIndividualsIncluded = group.individualIncluded && group.individualIncluded.length ? group.individualIncluded : [];
                if (groupIndividualsIncluded.length) {
                    groupIndividualsIncluded.forEach(individual => {
                        // Get group's individual evidence
                        totalObjects.push(filteredObject(individual));
                        // loop through group's individual scores
                        let groupIndividualScores = individual.scores && individual.scores.length ? individual.scores : [];
                        if (groupIndividualScores.length) {
                            groupIndividualScores.forEach(score => {
                                // Get scores
                                totalObjects.push(filteredObject(score));
                            });
                        }
                    });
                }
            });
        }

        // loop through families
        let families = annotation.families && annotation.families.length ? annotation.families : [];
        if (families.length) {
            families.forEach(family => {
                // Get family evidence
                totalObjects.push(filteredObject(family));
                // loop through individuals with each family
                let familyIndividualsIncluded = family.individualIncluded && family.individualIncluded.length ? family.individualIncluded : [];
                if (familyIndividualsIncluded.length) {
                    familyIndividualsIncluded.forEach(individual => {
                        // Get family's individual evidence
                        totalObjects.push(filteredObject(individual));
                        // loop through family's individual scores
                        let familyIndividualScores = individual.scores && individual.scores.length ? individual.scores : [];
                        if (familyIndividualScores.length) {
                            familyIndividualScores.forEach(score => {
                                // Get scores
                                totalObjects.push(filteredObject(score));
                            });
                        }
                    });
                }
            });
        }

        // loop through individuals
        let individuals = annotation.individuals && annotation.individuals.length ? annotation.individuals : [];
        if (individuals.length) {
            individuals.forEach(individual => {
                // Get individual evidence
                totalObjects.push(filteredObject(individual));
                // loop through individual scores
                let individualScores = individual.scores && individual.scores.length ? individual.scores : [];
                if (individualScores.length) {
                    individualScores.forEach(score => {
                        // Get scores
                        totalObjects.push(filteredObject(score));
                    });
                }
            });
        }

        // loop through experimentals
        let experimentals = annotation.experimentalData && annotation.experimentalData.length ? annotation.experimentalData : [];
        if (experimentals.length) {
            experimentals.forEach(experimental => {
                // Get individual evidence
                totalObjects.push(filteredObject(experimental));
                // loop through experimental scores
                let experimentalScores = experimental.scores && experimental.scores.length ? experimental.scores : [];
                if (experimentalScores.length) {
                    experimentalScores.forEach(score => {
                        // Get scores
                        totalObjects.push(filteredObject(score));
                    });
                }
            });
        }

        // loop through case-controls
        let caseControls = annotation.caseControlStudies && annotation.caseControlStudies.length ? annotation.caseControlStudies : [];
        if (caseControls.length) {
            caseControls.forEach(caseControl => {
                // Get case-control evidence
                totalObjects.push(filteredObject(caseControl));
                // Get case-control Case Cohort group
                if (caseControl.caseCohort) {
                    totalObjects.push(filteredObject(caseControl.caseCohort));
                }
                // Get case-control Control Cohort group
                if (caseControl.controlCohort) {
                    totalObjects.push(filteredObject(caseControl.controlCohort));
                }
                // loop through case-control scores
                let caseControlScores = caseControl.scores && caseControl.scores.length ? caseControl.scores : [];
                if (caseControlScores.length) {
                    caseControlScores.forEach(score => {
                        // Get scores
                        totalObjects.push(filteredObject(score));
                    });
                }
            });
        }
    });
    // Get provisionalClassifications objects
    let classifications = gdm.provisionalClassifications && gdm.provisionalClassifications.length ? gdm.provisionalClassifications : [];
    classifications.forEach(classification => {
        totalObjects.push(filteredObject(classification));
    });
    // Get provisionalClassifications objects
    let variantPathogenicity = gdm.variantPathogenicity && gdm.variantPathogenicity.length ? gdm.variantPathogenicity : [];
    variantPathogenicity.forEach(variant => {
        totalObjects.push(filteredObject(variant));
    });

    return totalObjects;
}

/**
 * Method to filter object keys
 * @param {object} target - A targeted data object in GDM
 */
function filteredObject(target) {
    const allowed = ['date_created', 'last_modified', 'submitted_by', '@type', 'affiliation', '@id'];

    const filtered = Object.keys(target)
        .filter(key => allowed.includes(key))
        .reduce((obj, key) => {
            obj[key] = target[key];
            return obj;
        }, {});

    return filtered;
}
<|endoftext|>


<commit_before>/* global L */

const isTrue = require('./isTrue')
const styleToLeaflet = require('./styleToLeaflet')
const parseLength = require('./parseLength')

class DecoratorPattern {
  constructor (layer) {
    this.layer = layer

    this.layer.on('update', this.processObject.bind(this))
    this.layer.on('remove', this.removeObject.bind(this))
  }

  parseType (key, value, twigData) {
    switch (key) {
      case 'polygon':
      case 'rotate':
        return isTrue(value)
      case 'pixelSize':
      case 'repeat':
      case 'offset':
      case 'endOffset':
        return parseLength(value, twigData.map.metersPerPixel)
      case 'angleCorrection':
      case 'headAngle':
        return parseFloat(value)
      default:
        return value
    }
  }

  processObject (object, data) {
    if (!data.patternFeatures) {
      data.patternFeatures = {}
    }

    for (var k in data.features) {
      const def = k === 'default' ? data.data.style : data.data['style:' + k]

      if (data.styles.includes(k)) {
        let patternTypes = {}
        let patternOptions = []
        let symbolOptions = {}

        for (const k in def) {
          const m = k.match(/^pattern([^-]*)$/)
          if (m) {
            patternTypes[m[1]] = def[k]
            patternOptions[m[1]] = {}
            symbolOptions[m[1]] = {}
          }
        }

        for (const k in def) {
          const m1 = k.match(/^pattern([^-]*)-path-(.*)$/)
          const m2 = k.match(/^pattern([^-]*)-(.*)$/)

          if (m1) {
            symbolOptions[m1[1]][m1[2]] = def[k]
          } else if (m2) {
            patternOptions[m2[1]][m2[2]] = this.parseType(m2[2], def[k], data.twigData)
          }
        }

        let patternIds = Object.keys(patternTypes)
        let patterns = []
        patternIds.forEach(patternId => {
          let symbol
          let options = patternOptions[patternId]
          options.pathOptions = styleToLeaflet(symbolOptions[patternId], data.twigData)

          switch (patternTypes[patternId]) {
            case 'dash':
              symbol = L.Symbol.dash(options)
              break
            case 'arrowHead':
              symbol = L.Symbol.arrowHead(options)
              break
            case 'marker':
              symbol = L.Symbol.marker(options)
              break
            default:
              // TODO
          }

          patternOptions[patternId].symbol = symbol
          patterns.push(patternOptions[patternId])
        })

        if (!data.patternFeatures[k]) {
          data.patternFeatures[k] = L.polylineDecorator(data.features[k])
          data.patternFeatures[k].addTo(this.layer.map)
        }

        data.patternFeatures[k].setPatterns(patterns)

        if (this.layer._shallBindPopupToStyle(k)) {
          data.patternFeatures[k].bindPopup(data.popup)
        }
      } else {
        if (data.patternFeatures[k]) {
          this.layer.map.removeLayer(data.patternFeatures[k])
          delete data.patternFeatures[k]
        }
      }
    }
  }

  removeObject (object, data) {
    if (!data.patternFeatures) {
      return
    }

    for (var k in data.features) {
      if (data.patternFeatures[k]) {
        this.layer.map.removeLayer(data.patternFeatures[k])
        delete data.patternFeatures[k]
      }
    }
  }
}

module.exports = DecoratorPattern
<commit_msg>Pattern: ignore unknown patterns<commit_after>/* global L */

const isTrue = require('./isTrue')
const styleToLeaflet = require('./styleToLeaflet')
const parseLength = require('./parseLength')

class DecoratorPattern {
  constructor (layer) {
    this.layer = layer

    this.layer.on('update', this.processObject.bind(this))
    this.layer.on('remove', this.removeObject.bind(this))
  }

  parseType (key, value, twigData) {
    switch (key) {
      case 'polygon':
      case 'rotate':
        return isTrue(value)
      case 'pixelSize':
      case 'repeat':
      case 'offset':
      case 'endOffset':
        return parseLength(value, twigData.map.metersPerPixel)
      case 'angleCorrection':
      case 'headAngle':
        return parseFloat(value)
      default:
        return value
    }
  }

  processObject (object, data) {
    if (!data.patternFeatures) {
      data.patternFeatures = {}
    }

    for (var k in data.features) {
      const def = k === 'default' ? data.data.style : data.data['style:' + k]

      if (data.styles.includes(k)) {
        let patternTypes = {}
        let patternOptions = []
        let symbolOptions = {}

        for (const k in def) {
          const m = k.match(/^pattern([^-]*)$/)
          if (m) {
            patternTypes[m[1]] = def[k]
            patternOptions[m[1]] = {}
            symbolOptions[m[1]] = {}
          }
        }

        for (const k in def) {
          const m1 = k.match(/^pattern([^-]*)-path-(.*)$/)
          const m2 = k.match(/^pattern([^-]*)-(.*)$/)

          if (m1) {
            symbolOptions[m1[1]][m1[2]] = def[k]
          } else if (m2) {
            patternOptions[m2[1]][m2[2]] = this.parseType(m2[2], def[k], data.twigData)
          }
        }

        let patternIds = Object.keys(patternTypes)
        let patterns = []
        patternIds.forEach(patternId => {
          let symbol
          let options = patternOptions[patternId]
          options.pathOptions = styleToLeaflet(symbolOptions[patternId], data.twigData)

          switch (patternTypes[patternId]) {
            case 'dash':
              symbol = L.Symbol.dash(options)
              break
            case 'arrowHead':
              symbol = L.Symbol.arrowHead(options)
              break
            case 'marker':
              symbol = L.Symbol.marker(options)
              break
            default:
              // TODO
          }

          if (symbol) {
            patternOptions[patternId].symbol = symbol
            patterns.push(patternOptions[patternId])
          }
        })

        if (!data.patternFeatures[k]) {
          data.patternFeatures[k] = L.polylineDecorator(data.features[k])
          data.patternFeatures[k].addTo(this.layer.map)
        }

        data.patternFeatures[k].setPatterns(patterns)

        if (this.layer._shallBindPopupToStyle(k)) {
          data.patternFeatures[k].bindPopup(data.popup)
        }
      } else {
        if (data.patternFeatures[k]) {
          this.layer.map.removeLayer(data.patternFeatures[k])
          delete data.patternFeatures[k]
        }
      }
    }
  }

  removeObject (object, data) {
    if (!data.patternFeatures) {
      return
    }

    for (var k in data.features) {
      if (data.patternFeatures[k]) {
        this.layer.map.removeLayer(data.patternFeatures[k])
        delete data.patternFeatures[k]
      }
    }
  }
}

module.exports = DecoratorPattern
<|endoftext|>


<commit_before>import {h, Component} 				from 'preact/preact';
import UIButton						from 'com/ui/button/button';
import marked 						from '../../internal/marked/marked';

class Autocompletions extends Component {
	constructor(props) {
		super(props);
		this.onKeyDown = this.onKeyDown.bind(this);
	}

	onKeyDown( e ) {
		switch (e.key) {
			case "Escape":
				this.handleAbort();
				return false;
			case "ArrowUp":
				this.updateSelected(-1);
				return false;
			case "ArrowDown":
				this.updateSelected(1);
				return false;
			case "Enter":
			case "Tab":
				this.selectSelected();
				return false;
		}
		return true;
	}

	componentWillReceiveProps( nextProps ) {
		let {text, cursorPos} = nextProps;
		if (text) {
			cursorPos = cursorPos ? cursorPos : 0;
			const matchObj = this.getMatch(text, cursorPos);
			//console.log('Autocomplete props', matchObj, `'${this.state.selected}'`, text);
			if (matchObj) {
				this.setState({
					'text': text,
					'cursorPos': cursorPos,
					'match': matchObj.match,
					'matchStart': matchObj.matchStart,
					'matchEnd': matchObj.matchEnd,
				});
			}
			else {
				this.setState({'match': null});
			}
		}
		else {
			this.setState({'match': null});
		}
	}

	getMatch( text, cursorPos ) {
		const {startPattern, endPattern} = this.state;
		const left = text.slice(0, cursorPos).match(startPattern);
		//console.log('Matching:', text, '|', left);
		if (!left) {
			return null;
		}
		const right = text.slice(cursorPos).match(endPattern);
		return {
			'match': left[2] + right[0],
			'matchStart': cursorPos - left[2].length,
			'matchEnd': cursorPos + right[0].length,
		};
	}

	selectSelected() {
		const {match, matchStart, matchEnd} = this.state;
		let {selected} = this.state;
		selected = this.getOptions(selected ? selected : match).sort((a, b) => b.score - a.score)[0].name;
		this.executeSelect(selected);
	}

	handleSelect( item, e ) {
		/* Bind per element so that the match/item is the first argument.
		*/
		this.executeSelect(item.name);
	}

	executeSelect(selected) {
		selected = `${selected} `;
		const {onSelect, text} = this.props;
		const {matchStart, matchEnd} = this.state;
		const updatedText = text.slice(0, matchStart) + selected + text.slice(matchEnd);
		this.setState({'text': updatedText, 'selected': selected});
		if (onSelect) {
			onSelect(updatedText, matchStart + selected.length);
		}
	}

	updateSelected(indexChange) {
		const {selectedIndex, options} = this.getSelected();
		let nextIndex = selectedIndex + indexChange;
		if (nextIndex < 0) {
			nextIndex = options.length - 1;
		}
		else {
			nextIndex %= options.length;
		}
		if (nextIndex < options.length) {
			this.setState({'selected': options[nextIndex].name});
		}
	}

	handleAbort() {
		this.setState({'selected': this.state.match});
	}

	getOptions( hint ) {
		/* Should return an array of objects that contains the
		fields `name` and `score`. Could have more fields if useful for the render function and callbacks.
		Name should be the exact word that is expected to be written in
		the input if option is selected.
		*/
		return [];
	}

	renderSuggestion(item, classModifier) {
		/* Should return a JSX element rendering the option
		*/
		return null;
	}

	getSelected() {
		const {match, selected} = this.state;
		let {maxItems} = this.state;
		if (!maxItems) {
			maxItems = 4;
		}
		let selectedIndex = selected ? -1 : 0;
		const options = this.getOptions(match).sort((a, b) => b.score - a.score).slice(0, maxItems);
		if (options) {
			const selectedMatch = options.map((opt, i) => [opt, i]).filter(item => item[0].name == selected);
			if (selectedMatch.length == 1) {
				selectedIndex = selectedMatch[0][1];
			}
		}
		return {'selected': selected, 'selectedIndex': selectedIndex, 'options': options};
	}

	render( props, state ) {
		const {match, name} = state;
		let {maxItems, selected} = state;

		if (match && match != selected) {
			let {selected, options, selectedIndex} = this.getSelected();
			if (options.length > 0 && (!selected || selectedIndex < 0)) {
				selected = options[0].name;
				if (selectedIndex < 0) {
					this.setState({'selected': selected});
				}
			}
			if (selected ? options.length > 1 || (options.length == 1 && options[0].name != match && match != selected) : options.length > 0) {
				props.captureKeyDown(name, this.onKeyDown);
				return (

						{options.map((m, i) => this.renderSuggestion(m, i == selectedIndex ? '-selected' : ''))}

				);
			}
			else {
				props.captureKeyDown(name, null);
			}
		}
	}
}

export class AutocompleteEmojis extends Autocompletions {
	constructor(props) {
		super(props);
		this.state = {
			'name': 'emojis',
			'startPattern': /([\s ]|^)(:[A-Za-z-_0-9]*)$/,
			'endPattern': /^([A-Za-z-_0-9]*:?)([\s ]+|$)/,
			'maxItems': 8,
			'mrkd': new marked(),
		};
	}

	getOptions(hint) {
		const {emojiList} = window.emoji;
		const options = [];
		const matcher = new RegExp(hint ? hint.trim().substr(1).replace(':', '').replace('-', '_') : '', 'i');
		for (let emoji in emojiList) {
			const matches = matcher.exec(emoji);
			if (hint.length == 0 || matches) {
				const matchStart = matches ? emoji.indexOf(matches[0]) + 1 : 0;
				const matchEnd = matches ? matchStart + matches[0].length : 0;
				let score = matchStart == 0 ? 0.5 : 1;
				score = Math.pow(hint.length / emoji.length, score);
				options.push({
					'name': ':' + emoji + ':',
					'score': score,
					'matchStart': matchStart,
					'matchEnd': matchEnd,
				});
			}
		}
		return options;
	}

	renderSuggestion(item, classModifier) {
		const {matchStart, matchEnd, name} = item;
		let ShowLeft = null;
		let ShowMatch = null;
		let ShowRight = null;
		if (matchEnd != matchStart) {
			ShowLeft = name.substr(0, matchStart);
			ShowMatch = {name.substr(matchStart, matchEnd - matchStart)};
			ShowRight = name.substr(matchEnd);
		}
		else {
			ShowLeft = name;
		}
		const {mrkd} = this.state;
		const ShowEmoji = mrkd.parse(name, {});

		return (
			<UIButton key={name} class={cN(classModifier)} onclick={this.handleSelect.bind(this, item)}>

{ShowEmoji}
{ShowLeft}{ShowMatch}{ShowRight}
			</UIButton>);
	}
}

export class AutocompleteAtNames extends Autocompletions {
	constructor(props) {
		super(props);
		this.state = {
			'name': 'at-names',
			'startPattern': /([\s ]|^)(@[A-Za-z-_0-9]*)$/,
			'endPattern': /^([A-Za-z-_0-9]*)([\s ]+|$)/,
			'maxItems': 8,
		};
	}

	getOptions(hint) {
		const {authors} = this.props;
		const options = [];
		const matcher = new RegExp(hint ? hint.trim().substr(1) : '', 'i');
		if (authors) {
			for (let author in authors) {
				const authorData = authors[author];
				const matches = matcher.exec(authorData.name);
				if (hint.length == 0 || matches) {
					const matchStart = matches ? authorData.name.indexOf(matches[0]) + 1 : 0;
					const matchEnd = matches ? matchStart + matches[0].length : 0;
					let score = matchStart == 0 ? 0.5 : 1;
					score = Math.pow(hint.length / authorData.name.length, score);
					options.push({
						'name': '@' + authorData.name,
						'score': score,
						'matchStart': matchStart,
						'matchEnd': matchEnd,
					});
				}
			}
		}
		return options;
	}

	renderSuggestion(item, classModifier) {
		const {matchStart, matchEnd, name} = item;
		let ShowLeft = null;
		let ShowMatch = null;
		let ShowRight = null;
		if (matchStart != matchEnd) {
			ShowLeft = name.substr(0, matchStart);
			ShowMatch = {name.substr(matchStart, matchEnd - matchStart)};
			ShowRight = name.substr(matchEnd);
		}
		else {
			ShowLeft = name;
		}
		return <UIButton key={name} class={cN(classModifier)} onclick={this.handleSelect.bind(this, item)}>{ShowLeft}{ShowMatch}{ShowRight}</UIButton>;
	}
}
<commit_msg>Fix competition bugs<commit_after>import {h, Component} 				from 'preact/preact';
import UIButton						from 'com/ui/button/button';
import marked 						from '../../internal/marked/marked';

class Autocompletions extends Component {
	constructor(props) {
		super(props);
		this.onKeyDown = this.onKeyDown.bind(this);
	}

	onKeyDown( e ) {
		const {match} = this.state;
		if (!match) {
			return true;
		}
		switch (e.key) {
			case "Escape":
				this.handleAbort();
				return false;
			case "ArrowUp":
				this.updateSelected(-1);
				return false;
			case "ArrowDown":
				this.updateSelected(1);
				return false;
			case "Enter":
			case "Tab":
				this.selectSelected();
				return false;
		}
		return true;
	}

	componentDidMount() {
		this.componentWillReceiveProps(this.props);
	}

	componentWillReceiveProps( nextProps ) {
		let {text, cursorPos} = nextProps;
		if (text) {
			cursorPos = cursorPos ? cursorPos : 0;
			const matchObj = this.getMatch(text, cursorPos);
			//console.log('Autocomplete props', matchObj, `'${this.state.selected}'`, text);
			if (matchObj) {
				this.setState({
					'text': text,
					'cursorPos': cursorPos,
					'match': matchObj.match,
					'matchStart': matchObj.matchStart,
					'matchEnd': matchObj.matchEnd,
				});
			}
			else {
				this.setState({'match': null});
			}
		}
		else {
			this.setState({'match': null});
		}
	}

	getMatch( text, cursorPos ) {
		const {startPattern, endPattern} = this.state;
		const left = text.slice(0, cursorPos).match(startPattern);
		//console.log('Matching:', text, '|', left);
		if (!left) {
			return null;
		}
		const right = text.slice(cursorPos).match(endPattern);
		return {
			'match': left[2] + right[0],
			'matchStart': cursorPos - left[2].length,
			'matchEnd': cursorPos + right[0].length,
		};
	}

	selectSelected() {
		const {match, matchStart, matchEnd} = this.state;
		if (!match) {
			return;
		}
		let {selected} = this.state;
		selected = this.getOptions(selected ? selected : match).sort((a, b) => b.score - a.score)[0].name;
		this.executeSelect(selected);
	}

	handleSelect( item, e ) {
		/* Bind per element so that the match/item is the first argument.
		*/
		const {match} = this.state;
		if (!match) {
			return;
		}

		this.executeSelect(item.name);
	}

	executeSelect(selected) {
		const {matchStart, matchEnd, match} = this.state;
		const {onSelect, text} = this.props;
		selected = `${selected} `;
		const updatedText = text.slice(0, matchStart) + selected + text.slice(matchEnd);
		this.setState({'text': updatedText, 'selected': selected});
		if (onSelect) {
			onSelect(updatedText, matchStart + selected.length);
		}
	}

	updateSelected(indexChange) {
		const {selectedIndex, options} = this.getSelected();
		let nextIndex = selectedIndex + indexChange;
		if (nextIndex < 0) {
			nextIndex = options.length - 1;
		}
		else {
			nextIndex %= options.length;
		}
		if (nextIndex < options.length) {
			this.setState({'selected': options[nextIndex].name});
		}
	}

	handleAbort() {
		this.setState({'selected': this.state.match});
	}

	getOptions( hint ) {
		/* Should return an array of objects that contains the
		fields `name` and `score`. Could have more fields if useful for the render function and callbacks.
		Name should be the exact word that is expected to be written in
		the input if option is selected.
		*/
		return [];
	}

	renderSuggestion(item, classModifier) {
		/* Should return a JSX element rendering the option
		*/
		return null;
	}

	getSelected() {
		const {match, selected} = this.state;
		let {maxItems} = this.state;
		if (!maxItems) {
			maxItems = 4;
		}
		let selectedIndex = selected ? -1 : 0;
		const options = this.getOptions(match).sort((a, b) => b.score - a.score).slice(0, maxItems);
		if (options) {
			const selectedMatch = options.map((opt, i) => [opt, i]).filter(item => item[0].name == selected);
			if (selectedMatch.length == 1) {
				selectedIndex = selectedMatch[0][1];
			}
		}
		return {'selected': selected, 'selectedIndex': selectedIndex, 'options': options};
	}

	render( props, state ) {
		const {match, name} = state;
		let {maxItems, selected} = state;
		if (match && match != selected) {
			let {selected, options, selectedIndex} = this.getSelected();
			if (options.length > 0 && (!selected || selectedIndex < 0)) {
				selected = options[0].name;
				if (selectedIndex < 0) {
					this.setState({'selected': selected});
				}
			}
			if (selected ? options.length > 1 || (options.length == 1 && options[0].name != match && match != selected) : options.length > 0) {
				props.captureKeyDown(name, this.onKeyDown);
				return (

						{options.map((m, i) => this.renderSuggestion(m, i == selectedIndex ? '-selected' : ''))}

				);
			}
			else {
				props.captureKeyDown(name, null);
			}
		}
	}
}

export class AutocompleteEmojis extends Autocompletions {
	constructor(props) {
		super(props);
		this.state = {
			'name': 'emojis',
			'startPattern': /([\s ]|^)(:[A-Za-z-_0-9]*)$/,
			'endPattern': /^([A-Za-z-_0-9]*:?)([\s ]+|$)/,
			'maxItems': 8,
			'mrkd': new marked(),
		};
	}

	getOptions(hint) {
		const {emojiList} = window.emoji;
		const options = [];
		const matcher = new RegExp(hint ? hint.trim().substr(1).replace(':', '').replace('-', '_') : '', 'i');
		for (let emoji in emojiList) {
			const matches = matcher.exec(emoji);
			if (hint.length == 0 || matches) {
				const matchStart = matches ? emoji.indexOf(matches[0]) + 1 : 0;
				const matchEnd = matches ? matchStart + matches[0].length : 0;
				let score = matchStart == 0 ? 0.5 : 1;
				score = Math.pow(hint.length / emoji.length, score);
				options.push({
					'name': ':' + emoji + ':',
					'score': score,
					'matchStart': matchStart,
					'matchEnd': matchEnd,
				});
			}
		}
		return options;
	}

	renderSuggestion(item, classModifier) {
		const {matchStart, matchEnd, name} = item;
		let ShowLeft = null;
		let ShowMatch = null;
		let ShowRight = null;
		if (matchEnd != matchStart) {
			ShowLeft = name.substr(0, matchStart);
			ShowMatch = {name.substr(matchStart, matchEnd - matchStart)};
			ShowRight = name.substr(matchEnd);
		}
		else {
			ShowLeft = name;
		}
		const {mrkd} = this.state;
		const ShowEmoji = mrkd.parse(name, {});

		return (
			<UIButton key={name} class={cN(classModifier)} onclick={this.handleSelect.bind(this, item)}>

{ShowEmoji}
{ShowLeft}{ShowMatch}{ShowRight}
			</UIButton>);
	}
}

export class AutocompleteAtNames extends Autocompletions {
	constructor(props) {
		super(props);
		this.state = {
			'name': 'at-names',
			'startPattern': /([\s ]|^)(@[A-Za-z-_0-9]*)$/,
			'endPattern': /^([A-Za-z-_0-9]*)([\s ]+|$)/,
			'maxItems': 8,
		};
	}

	getOptions(hint) {
		const {authors} = this.props;
		const options = [];
		const matcher = new RegExp(hint ? hint.trim().substr(1) : '', 'i');
		if (authors) {
			for (let author in authors) {
				const authorData = authors[author];
				const matches = matcher.exec(authorData.name);
				if (hint.length == 0 || matches) {
					const matchStart = matches ? authorData.name.indexOf(matches[0]) + 1 : 0;
					const matchEnd = matches ? matchStart + matches[0].length : 0;
					let score = matchStart == 0 ? 0.5 : 1;
					score = Math.pow(hint.length / authorData.name.length, score);
					options.push({
						'name': '@' + authorData.name,
						'score': score,
						'matchStart': matchStart,
						'matchEnd': matchEnd,
					});
				}
			}
		}
		return options;
	}

	renderSuggestion(item, classModifier) {
		const {matchStart, matchEnd, name} = item;
		let ShowLeft = null;
		let ShowMatch = null;
		let ShowRight = null;
		if (matchStart != matchEnd) {
			ShowLeft = name.substr(0, matchStart);
			ShowMatch = {name.substr(matchStart, matchEnd - matchStart)};
			ShowRight = name.substr(matchEnd);
		}
		else {
			ShowLeft = name;
		}
		return <UIButton key={name} class={cN(classModifier)} onclick={this.handleSelect.bind(this, item)}>{ShowLeft}{ShowMatch}{ShowRight}</UIButton>;
	}
}
<|endoftext|>


<commit_before>import PropTypes from "prop-types";
import React from "react";
import styled, { keyframes } from "styled-components";

import style from "@agir/front/genericComponents/_variables.scss";

import Link from "@agir/front/app/Link";

import { ICONS, getIconDataUrl } from "./utils";

const spinner = keyframes`
  0% {
    transform: rotate(0turn);
    stroke-dashoffset: 0.662em;
  }
  50% {
    transform: rotate(2turn);
    stroke-dashoffset: 3.138em;
  }
  100% {
    transform: rotate(3turn);
    stroke-dashoffset: 0.662em;
  }
`;

export const ButtonLoader = styled.i
  .withConfig({
    shouldForwardProp: (prop) => ["loading", "theme"].includes(prop) === false,
  })
  .attrs((attrs) => ({
    ...attrs,
    children: (
      <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
        <circle cx="8" cy="8" r="7" />
      </svg>
    ),
  }))`
  position: absolute;
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  background-color: inherit;
  z-index: ${({ loading }) => (loading ? 1 : -1)};

  & > svg {
    opacity: ${({ loading }) => (loading ? 1 : 0)};
    transition: opacity, width, height 150ms ease-in-out;
    width: ${({ loading }) => (loading ? 1 : 0)}em;
    height: ${({ loading }) => (loading ? 1 : 0)}em;

    circle {
        fill: transparent;
        stroke: currentColor;
        stroke-width: 2;
        stroke-linecap: round;
        stroke-dasharray: 3.138em;
        transform-origin: center center;
        animation-name: ${({ loading }) => (loading ? spinner : "none")};
        animation-iteration-count: infinite;
        animation-easing-function: linear;
        animation-direction: forwards;
        animation-duration: 3s;
    }
  }

  & + span {
    transition: opacity 250ms ease-in-out;
    opacity: ${({ loading }) => (loading ? 0 : 1)};
  }
`;

export const BaseButton = styled.button
  .withConfig({
    shouldForwardProp: (prop) =>
      ["loading", "link", "small", "block", "wrap"].includes(prop) === false,
  })
  .attrs(({ link, as, loading, children, ...rest }) => ({
    ...rest,
    as: link ? Link : as,
    children: (
      <>
        <ButtonLoader loading={loading} />
        {children}
      </>
    ),
  }))`
  margin: 0;
  justify-content: center;
  align-items: center;
  text-align: center;
  font-weight: 500;
  border: 1px solid;
  cursor: pointer;
  position: relative;

  white-space: ${({ wrap }) => (wrap ? "normal" : "nowrap")};
  text-overflow: ${({ wrap }) => (wrap ? "inherit" : "ellipsis")};
  overflow: ${({ wrap }) => (wrap ? "auto" : "hidden")};

  display: ${({ block }) => (block ? "flex" : "inline-flex")};
  width: ${({ block }) => (block ? "100%" : "auto")};

  padding: ${({ small }) => (small ? "0.5rem 0.75rem" : "0.75rem 1.5rem")};
  line-height: ${({ small }) => (small ? "1" : "1.5")};
  font-size: ${({ small }) => (small ? "0.875rem" : "1rem")};

  border-radius: ${style.borderRadius};

  background-color: ${style.black50};
  border-color: ${style.black50};
  color: ${style.black1000};

  &:hover,
  &:focus,
  &:active {
    text-decoration: none;
  }

  &[disabled],
  &[disabled]:hover,
  &[disabled]:focus,
  &[disabled]:active {
    cursor: not-allowed;
  }

  &:before {
    flex: 0 0 auto;
    background-image: ${getIconDataUrl({ color: style.black1000 })};
    content: "";
    height: 1rem;
    width: 1rem;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center center;
    margin-right: ${({ small }) => (small ? "0.375rem" : "0.5rem")};
    display: ${({ icon }) => (icon && ICONS[icon] ? "inline-block" : "none")};
  }

  & > * {
    display: inline-flex;
    align-items: center;
    justify-content: center;

    & > * {
      flex: 0 0 auto;
    }
  }
}}
`;

BaseButton.propTypes = {
  link: PropTypes.bool,
  disabled: PropTypes.bool,
  small: PropTypes.bool,
  block: PropTypes.bool,
  wrap: PropTypes.bool,
  loading: PropTypes.bool,
  icon: PropTypes.string,
};
BaseButton.defaultProps = {
  link: false,
  disabled: false,
  small: false,
  block: false,
  wrap: false,
  loading: false,
};

export default BaseButton;
<commit_msg>fix: position button loader to avoid scroll<commit_after>import PropTypes from "prop-types";
import React from "react";
import styled, { keyframes } from "styled-components";

import style from "@agir/front/genericComponents/_variables.scss";

import Link from "@agir/front/app/Link";

import { ICONS, getIconDataUrl } from "./utils";

const spinner = keyframes`
  0% {
    transform: rotate(0turn);
    stroke-dashoffset: 0.662em;
  }
  50% {
    transform: rotate(2turn);
    stroke-dashoffset: 3.138em;
  }
  100% {
    transform: rotate(3turn);
    stroke-dashoffset: 0.662em;
  }
`;

export const ButtonLoader = styled.i
  .withConfig({
    shouldForwardProp: (prop) => ["loading", "theme"].includes(prop) === false,
  })
  .attrs((attrs) => ({
    ...attrs,
    children: (
      <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
        <circle cx="8" cy="8" r="7" />
      </svg>
    ),
  }))`
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  padding: 0;
  margin: 0;
  background-color: inherit;
  z-index: ${({ loading }) => (loading ? 1 : -1)};

  & > svg {
    opacity: ${({ loading }) => (loading ? 1 : 0)};
    transition: opacity, width, height 150ms ease-in-out;
    width: ${({ loading }) => (loading ? 1 : 0)}em;
    height: ${({ loading }) => (loading ? 1 : 0)}em;

    circle {
        fill: transparent;
        stroke: currentColor;
        stroke-width: 2;
        stroke-linecap: round;
        stroke-dasharray: 3.138em;
        transform-origin: center center;
        animation-name: ${({ loading }) => (loading ? spinner : "none")};
        animation-iteration-count: infinite;
        animation-easing-function: linear;
        animation-direction: forwards;
        animation-duration: 3s;
    }
  }

  & + span {
    transition: opacity 250ms ease-in-out;
    opacity: ${({ loading }) => (loading ? 0 : 1)};
  }
`;

export const BaseButton = styled.button
  .withConfig({
    shouldForwardProp: (prop) =>
      ["loading", "link", "small", "block", "wrap"].includes(prop) === false,
  })
  .attrs(({ link, as, loading, children, ...rest }) => ({
    ...rest,
    as: link ? Link : as,
    children: (
      <>
        <ButtonLoader loading={loading} />
        {children}
      </>
    ),
  }))`
  margin: 0;
  justify-content: center;
  align-items: center;
  text-align: center;
  font-weight: 500;
  border: 1px solid;
  cursor: pointer;
  position: relative;

  white-space: ${({ wrap }) => (wrap ? "normal" : "nowrap")};
  text-overflow: ${({ wrap }) => (wrap ? "inherit" : "ellipsis")};
  overflow: ${({ wrap }) => (wrap ? "auto" : "hidden")};

  display: ${({ block }) => (block ? "flex" : "inline-flex")};
  width: ${({ block }) => (block ? "100%" : "auto")};

  padding: ${({ small }) => (small ? "0.5rem 0.75rem" : "0.75rem 1.5rem")};
  line-height: ${({ small }) => (small ? "1" : "1.5")};
  font-size: ${({ small }) => (small ? "0.875rem" : "1rem")};

  border-radius: ${style.borderRadius};

  background-color: ${style.black50};
  border-color: ${style.black50};
  color: ${style.black1000};

  &:hover,
  &:focus,
  &:active {
    text-decoration: none;
  }

  &[disabled],
  &[disabled]:hover,
  &[disabled]:focus,
  &[disabled]:active {
    cursor: not-allowed;
  }

  &:before {
    flex: 0 0 auto;
    background-image: ${getIconDataUrl({ color: style.black1000 })};
    content: "";
    height: 1rem;
    width: 1rem;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center center;
    margin-right: ${({ small }) => (small ? "0.375rem" : "0.5rem")};
    display: ${({ icon }) => (icon && ICONS[icon] ? "inline-block" : "none")};
  }

  & > * {
    display: inline-flex;
    align-items: center;
    justify-content: center;

    & > * {
      flex: 0 0 auto;
    }
  }
}}
`;

BaseButton.propTypes = {
  link: PropTypes.bool,
  disabled: PropTypes.bool,
  small: PropTypes.bool,
  block: PropTypes.bool,
  wrap: PropTypes.bool,
  loading: PropTypes.bool,
  icon: PropTypes.string,
};
BaseButton.defaultProps = {
  link: false,
  disabled: false,
  small: false,
  block: false,
  wrap: false,
  loading: false,
};

export default BaseButton;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import fuzzyFilterFactory from 'react-fuzzy-filter'
import { selectors as entitiesSelector } from '../../redux/entities'
import { actions as editorActions } from '../../redux/editor'
import { entitySets, entityTreeIconResolvers } from '../../lib/configuration'
import styles from './EntityFuzzyFinderModal.scss'

const { InputFilter, FilterResults } = fuzzyFilterFactory()

const fuseConfig = {
  shouldSort: true,
  includeScore: true,
  includeMatches: true,
  keys: ['path', 'name']
}

const resolveEntityTreeIconStyle = (entity) => {
  for (const k in entityTreeIconResolvers) {
    const mode = entityTreeIconResolvers[k](entity)
    if (mode) {
      return mode
    }
  }

  return null
}

class EntityFuzzyFinderModal extends Component {
  static propTypes = {
    close: PropTypes.func.isRequired,
    options: PropTypes.object.isRequired
  }

  constructor (props) {
    super(props)

    this.state = {
      selectedIndex: 0
    }

    this.lastResults = null

    this.setInputNode = this.setInputNode.bind(this)
    this.setResulItemNode = this.setResulItemNode.bind(this)
    this.getItem = this.getItem.bind(this)
    this.handleKeyDown = this.handleKeyDown.bind(this)
    this.renderResults = this.renderResults.bind(this)
  }

  componentDidMount () {
    setTimeout(() => this.inputNode.focus(), 0)
  }

  setInputNode (el) {
    this.inputNode = el
  }

  setResulItemNode (el, idx) {
    this[`resultItemNode${idx}`] = el
  }

  getItem (itemResult) {
    return itemResult.hasOwnProperty('_id') ? itemResult : itemResult.item
  }

  openEntity (entity) {
    this.props.openTab({ _id: entity._id })
    this.props.close()
  }

  handleKeyDown (ev) {
    const lastResults = this.lastResults
    const { selectedIndex } = this.state
    const scrollOpts = { block: 'nearest', inline: 'nearest' }

    if (ev.keyCode === 40 && selectedIndex < lastResults.length - 1) {
      const newIndex = selectedIndex + 1

      // up arrow
      this.setState({
        selectedIndex: newIndex
      })

      this[`resultItemNode${newIndex}`].scrollIntoView(scrollOpts)
    } else if (ev.keyCode === 38 && selectedIndex > 0) {
      const newIndex = selectedIndex - 1

      // down arrow
      this.setState({
        selectedIndex: newIndex
      })

      this[`resultItemNode${newIndex}`].scrollIntoView(scrollOpts)
    } else if (ev.keyCode === 13) {
      // enter
      if (lastResults[selectedIndex]) {
        this.openEntity(this.getItem(lastResults[selectedIndex]).entity)
      }

      this.setState({
        selectedIndex: 0
      })
    }
  }

  renderItemName (item, keyMatch) {
    const parentPath = `/${item.path.split('/').slice(1, -1).join('/')}`
    const elements = []

    if (keyMatch) {
      // +1 because the "/" at the end was removed
      const nameStartIndex = parentPath === '/' ? 1 : parentPath.length + 1
      const nameIndices = []
      const nameElements = []
      const pathIndices = []
      const pathElements = []

      keyMatch.indices.forEach((ind) => {
        const maxIndexForPath = parentPath.length - 1
        const maxIndexForName = item.path.length - 1

        const inRangeOfName = (
          ind[1] >= nameStartIndex &&
          ind[1] <= maxIndexForName
        )

        const inRangeOfFullPath = (
          ind[0] >= 0 &&
          ind[0] <= maxIndexForPath
        )

        if (inRangeOfName) {
          // we substract "- nameStartIndex" here because the indices must be relative
          // to item.name string
          nameIndices.push([
            (ind[0] <= nameStartIndex ? nameStartIndex : ind[0]) - nameStartIndex,
            ind[1] - nameStartIndex
          ])
        }

        if (inRangeOfFullPath) {
          pathIndices.push([ind[0], ind[1] >= maxIndexForPath ? maxIndexForPath : ind[1]])
        }
      })

      if (nameIndices.length > 0) {
        const maxIndexForName = item.name.length - 1
        let lastNameIndex = 0

        nameIndices.forEach(([indexStart, indexEnd], idx) => {
          if (lastNameIndex !== indexStart) {
            nameElements.push(
              <span key={`entity-name${lastNameIndex}/${indexStart - 1}`}>
                {item.name.slice(lastNameIndex, indexStart)}

            )
          }

          nameElements.push(
            <span key={`entity-name${indexStart}/${indexEnd}`} style={{ color: '#000' }}>
              {item.name.slice(indexStart, indexEnd + 1)}

          )

          lastNameIndex = indexEnd + 1

          if (idx === nameIndices.length - 1) {
            const resString = item.name.slice(lastNameIndex, maxIndexForName + 1)

            if (resString !== '') {
              nameElements.push(
                <span key={`entity-name${lastNameIndex}/${maxIndexForName}`}>
                  {resString}

              )
            }
          }
        })
      } else {
        nameElements.push(
          item.name
        )
      }

      if (pathIndices.length > 0) {
        const maxIndexForPath = parentPath.length - 1
        let lastPathIndex = 0

        pathIndices.forEach(([indexStart, indexEnd], idx) => {
          if (lastPathIndex !== indexStart) {
            pathElements.push(
              <span key={`entity-path${lastPathIndex}/${indexStart - 1}`}>
                {parentPath.slice(lastPathIndex, indexStart)}

            )
          }

          pathElements.push(
            <span key={`entity-path${indexStart}/${indexEnd}`}>
              {parentPath.slice(indexStart, indexEnd + 1)}

          )

          lastPathIndex = indexEnd + 1

          if (idx === pathIndices.length - 1) {
            const resString = parentPath.slice(lastPathIndex, maxIndexForPath + 1)

            if (resString !== '') {
              pathElements.push(
                <span key={`entity-path${lastPathIndex}/${maxIndexForPath}`}>
                  {resString}

              )
            }
          }
        })
      } else {
        pathElements.push(
          parentPath
        )
      }

      elements.push(
        {nameElements}
      )

      elements.push(
        {pathElements}
      )
    } else {
      elements.push(
        {item.name}
      )

      elements.push(
        {parentPath}
      )
    }

    return elements
  }

  renderResults (filteredItems) {
    // limit results to max 10 items
    const itemsToRender = filteredItems.slice(0, 10)
    const { selectedIndex } = this.state
    let content

    this.lastResults = itemsToRender

    if (itemsToRender.length > 0) {
      content = itemsToRender.map((current, idx) => {
        const item = this.getItem(current)
        const isActive = selectedIndex === idx
        const iconStyle = resolveEntityTreeIconStyle(item.entity) || (entitySets[item.entity.__entitySet].faIcon || styles.resultsItemDefaultIcon)
        const keyMatch = (current.hasOwnProperty('_id') ? [] : current.matches).find((m) => m.key === 'path')

        return (
          <div
            key={item.path}
            className={`${styles.resultsItem} ${isActive ? styles.active : ''}`}
            title={item.path}
            ref={(el) => this.setResulItemNode(el, idx)}
            onClick={() => this.openEntity(item.entity)}
          >
            {iconStyle && (
              <i key='entity-icon' className={`${styles.resultsItemIcon} fa ${iconStyle || ''}`} />
            )}
            {this.renderItemName(item, keyMatch)}

        )
      })
    } else {
      content = (

          No results

      )
    }

    return (

        {content}

    )
  }

  render () {
    const { entities } = this.props

    return (

          <InputFilter
            debounceTime={200}
            onChange={() => this.setState({ selectedIndex: 0 })}
            inputProps={{
              ref: this.setInputNode,
              placeholder: 'Ex: Orders, Invoice, /samples, /samples/Population',
              className: styles.input
            }}
          />
          <FilterResults
            items={entities}
            fuseConfig={fuseConfig}
          >
            {this.renderResults}
          </FilterResults>

    )
  }
}

export default Object.assign(connect((state) => ({
  entities: entitiesSelector.getNormalizedEntities(state)
}), {
  openTab: editorActions.openTab
})(EntityFuzzyFinderModal), { frameless: true })
<commit_msg>fix issue with entity navigation modal<commit_after>import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import fuzzyFilterFactory from 'react-fuzzy-filter'
import { selectors as entitiesSelector } from '../../redux/entities'
import { actions as editorActions } from '../../redux/editor'
import { entitySets, entityTreeIconResolvers } from '../../lib/configuration'
import styles from './EntityFuzzyFinderModal.scss'

const { InputFilter, FilterResults } = fuzzyFilterFactory()

const fuseConfig = {
  shouldSort: true,
  includeScore: true,
  includeMatches: true,
  keys: ['path', 'name']
}

const resolveEntityTreeIconStyle = (entity) => {
  for (const k in entityTreeIconResolvers) {
    const mode = entityTreeIconResolvers[k](entity)
    if (mode) {
      return mode
    }
  }

  return null
}

class EntityFuzzyFinderModal extends Component {
  static propTypes = {
    close: PropTypes.func.isRequired,
    options: PropTypes.object.isRequired
  }

  constructor (props) {
    super(props)

    this.state = {
      selectedIndex: 0
    }

    this.lastResults = null

    this.setInputNode = this.setInputNode.bind(this)
    this.setResulItemNode = this.setResulItemNode.bind(this)
    this.getItem = this.getItem.bind(this)
    this.handleKeyDown = this.handleKeyDown.bind(this)
    this.renderResults = this.renderResults.bind(this)
  }

  componentDidMount () {
    setTimeout(() => this.inputNode.focus(), 0)
  }

  setInputNode (el) {
    this.inputNode = el
  }

  setResulItemNode (el, idx) {
    this[`resultItemNode${idx}`] = el
  }

  getItem (itemResult) {
    return itemResult.hasOwnProperty('_id') ? itemResult : itemResult.item
  }

  openEntity (entity) {
    this.props.openTab({ _id: entity._id })
    this.props.close()
  }

  handleKeyDown (ev) {
    const lastResults = this.lastResults
    const { selectedIndex } = this.state
    const { close } = this.props
    const scrollOpts = { block: 'nearest', inline: 'nearest' }

    if (ev.keyCode === 40 && selectedIndex < lastResults.length - 1) {
      ev.preventDefault()
      ev.stopPropagation()

      const newIndex = selectedIndex + 1

      // up arrow
      this.setState({
        selectedIndex: newIndex
      })

      this[`resultItemNode${newIndex}`].scrollIntoView(scrollOpts)
    } else if (ev.keyCode === 38 && selectedIndex > 0) {
      ev.preventDefault()
      ev.stopPropagation()

      const newIndex = selectedIndex - 1

      // down arrow
      this.setState({
        selectedIndex: newIndex
      })

      this[`resultItemNode${newIndex}`].scrollIntoView(scrollOpts)
    } else if (ev.keyCode === 13) {
      ev.preventDefault()
      ev.stopPropagation()

      // enter
      if (lastResults[selectedIndex]) {
        this.openEntity(this.getItem(lastResults[selectedIndex]).entity)
      }

      this.setState({
        selectedIndex: 0
      })

      close()
    }
  }

  renderItemName (item, keyMatch) {
    const parentPath = `/${item.path.split('/').slice(1, -1).join('/')}`
    const elements = []

    if (keyMatch) {
      // +1 because the "/" at the end was removed
      const nameStartIndex = parentPath === '/' ? 1 : parentPath.length + 1
      const nameIndices = []
      const nameElements = []
      const pathIndices = []
      const pathElements = []

      keyMatch.indices.forEach((ind) => {
        const maxIndexForPath = parentPath.length - 1
        const maxIndexForName = item.path.length - 1

        const inRangeOfName = (
          ind[1] >= nameStartIndex &&
          ind[1] <= maxIndexForName
        )

        const inRangeOfFullPath = (
          ind[0] >= 0 &&
          ind[0] <= maxIndexForPath
        )

        if (inRangeOfName) {
          // we substract "- nameStartIndex" here because the indices must be relative
          // to item.name string
          nameIndices.push([
            (ind[0] <= nameStartIndex ? nameStartIndex : ind[0]) - nameStartIndex,
            ind[1] - nameStartIndex
          ])
        }

        if (inRangeOfFullPath) {
          pathIndices.push([ind[0], ind[1] >= maxIndexForPath ? maxIndexForPath : ind[1]])
        }
      })

      if (nameIndices.length > 0) {
        const maxIndexForName = item.name.length - 1
        let lastNameIndex = 0

        nameIndices.forEach(([indexStart, indexEnd], idx) => {
          if (lastNameIndex !== indexStart) {
            nameElements.push(
              <span key={`entity-name${lastNameIndex}/${indexStart - 1}`}>
                {item.name.slice(lastNameIndex, indexStart)}

            )
          }

          nameElements.push(
            <span key={`entity-name${indexStart}/${indexEnd}`} style={{ color: '#000' }}>
              {item.name.slice(indexStart, indexEnd + 1)}

          )

          lastNameIndex = indexEnd + 1

          if (idx === nameIndices.length - 1) {
            const resString = item.name.slice(lastNameIndex, maxIndexForName + 1)

            if (resString !== '') {
              nameElements.push(
                <span key={`entity-name${lastNameIndex}/${maxIndexForName}`}>
                  {resString}

              )
            }
          }
        })
      } else {
        nameElements.push(
          item.name
        )
      }

      if (pathIndices.length > 0) {
        const maxIndexForPath = parentPath.length - 1
        let lastPathIndex = 0

        pathIndices.forEach(([indexStart, indexEnd], idx) => {
          if (lastPathIndex !== indexStart) {
            pathElements.push(
              <span key={`entity-path${lastPathIndex}/${indexStart - 1}`}>
                {parentPath.slice(lastPathIndex, indexStart)}

            )
          }

          pathElements.push(
            <span key={`entity-path${indexStart}/${indexEnd}`}>
              {parentPath.slice(indexStart, indexEnd + 1)}

          )

          lastPathIndex = indexEnd + 1

          if (idx === pathIndices.length - 1) {
            const resString = parentPath.slice(lastPathIndex, maxIndexForPath + 1)

            if (resString !== '') {
              pathElements.push(
                <span key={`entity-path${lastPathIndex}/${maxIndexForPath}`}>
                  {resString}

              )
            }
          }
        })
      } else {
        pathElements.push(
          parentPath
        )
      }

      elements.push(
        {nameElements}
      )

      elements.push(
        {pathElements}
      )
    } else {
      elements.push(
        {item.name}
      )

      elements.push(
        {parentPath}
      )
    }

    return elements
  }

  renderResults (filteredItems) {
    // limit results to max 10 items
    const itemsToRender = filteredItems.slice(0, 10)
    const { selectedIndex } = this.state
    let content

    this.lastResults = itemsToRender

    if (itemsToRender.length > 0) {
      content = itemsToRender.map((current, idx) => {
        const item = this.getItem(current)
        const isActive = selectedIndex === idx
        const iconStyle = resolveEntityTreeIconStyle(item.entity) || (entitySets[item.entity.__entitySet].faIcon || styles.resultsItemDefaultIcon)
        const keyMatch = (current.hasOwnProperty('_id') ? [] : current.matches).find((m) => m.key === 'path')

        return (
          <div
            key={item.path}
            className={`${styles.resultsItem} ${isActive ? styles.active : ''}`}
            title={item.path}
            ref={(el) => this.setResulItemNode(el, idx)}
            onClick={() => this.openEntity(item.entity)}
          >
            {iconStyle && (
              <i key='entity-icon' className={`${styles.resultsItemIcon} fa ${iconStyle || ''}`} />
            )}
            {this.renderItemName(item, keyMatch)}

        )
      })
    } else {
      content = (

          No results

      )
    }

    return (

        {content}

    )
  }

  render () {
    const { entities } = this.props

    return (

          <InputFilter
            debounceTime={200}
            onChange={() => this.setState({ selectedIndex: 0 })}
            inputProps={{
              ref: this.setInputNode,
              placeholder: 'Ex: Orders, Invoice, /samples, /samples/Population',
              className: styles.input
            }}
          />
          <FilterResults
            items={entities}
            fuseConfig={fuseConfig}
          >
            {this.renderResults}
          </FilterResults>

    )
  }
}

export default Object.assign(connect((state) => ({
  entities: entitiesSelector.getNormalizedEntities(state)
}), {
  openTab: editorActions.openTab
})(EntityFuzzyFinderModal), { frameless: true })
<|endoftext|>


<commit_before>/*
Copyright (C) 2017  Cloudbase Solutions SRL

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

import React, { Component, PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './WizardNetworks.scss';
import Dropdown from '../NewDropdown';
import WizardActions from '../../actions/WizardActions';
import LoadingIcon from '../LoadingIcon';
import ConnectionsActions from '../../actions/ConnectionsActions';

const title = 'Network mapping';

class WizardNetworks extends Component {

  static contextTypes = {
    onSetTitle: PropTypes.func.isRequired,
  };

  static propTypes = {
    data: PropTypes.object,
    setWizardState: PropTypes.func
  }

  constructor(props) {
    super(props)

    this.networkOptions = null // [{ label: "Create new", value: null }]
    ConnectionsActions.loadNetworks(this.props.data.targetCloud.credential)

    props.data.selectedInstances.forEach((vm) => {
      ConnectionsActions.loadInstanceDetail({ id: this.props.data.sourceCloud.credential.id }, vm)
    })

    let valid = true
    if (props.data.networks) {
      props.data.networks.forEach(item => {
        if (item.migrateNetwork === null) {
          valid = false
        }
      })
    } else {
      valid = false
    }

    this.state = {
      networks: props.data.networks || null,
      nextStep: "WizardSchedule",
      valid: valid
    }
  }

  componentWillMount() {
    WizardActions.updateWizardState(this.state)
    this.context.onSetTitle(title);
  }

  componentWillReceiveProps(props) {
    this.processProps(props)
  }

  processProps(props) {
    let networks = []

    props.data.selectedInstances.forEach((vm) => {
      if (vm.devices && vm.devices.nics) {
        vm.devices.nics.forEach((item) => {
          let exists = false
          networks.forEach(network => {
            if (network.network_name == item.network_name) {
              exists = true
            }
          })
          if (!exists) {
            if (!item.migrateNetwork) {
              item.migrateNetwork = null
            }
            networks.push(item)
          }
        })
      }
    })

    if (networks.length == 0) {
      networks = null
    }
    if (props.data.targetNetworks && props.data.targetNetworks.length) {
      this.networkOptions = []
      props.data.targetNetworks.forEach((network) => {
        this.networkOptions.push({
          label: network.name,
          value: network.name
        })
      }, this)
    }
    this.setState({ networks: networks })
  }

  handleChangeNetwork(event, network) {
    let index = this.state.networks.indexOf(network)
    let valid = true
    let networks = this.state.networks
    networks[index].migrateNetwork = event.value
    networks.forEach(item => {
      if (item.migrateNetwork === null) {
        valid = false
      }
    })

    this.setState({
      networks: networks,
      valid: valid
    }, () => {
      WizardActions.updateWizardState(this.state)
    })
  }

  render() {
    if (this.state.networks != null) {
      let networks = this.state.networks.map((network, index) => {
        let networkDropdown
        if (this.networkOptions == null) {
          networkDropdown = "Searching for networks ..."
        } else if (this.networkOptions.length) {
          networkDropdown = (<Dropdown
            options={this.networkOptions}
            onChange={(e) => this.handleChangeNetwork(e, network)}
            value={network.migrateNetwork}
          />)
        } else {
          networkDropdown = "Could not find networks"
        }
        return (

                {network.network_name}

              {networkDropdown}

        )
      }, this)

      return (

              {networks}

      );
    } else {
      return (

              <LoadingIcon text="Loading networks..." />

      );
    }
  }

}

export default withStyles(WizardNetworks, s);
<commit_msg>Sorts networks by name CORWEB-53<commit_after>/*
Copyright (C) 2017  Cloudbase Solutions SRL

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

import React, { Component, PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './WizardNetworks.scss';
import Dropdown from '../NewDropdown';
import WizardActions from '../../actions/WizardActions';
import LoadingIcon from '../LoadingIcon';
import ConnectionsActions from '../../actions/ConnectionsActions';

const title = 'Network mapping';

class WizardNetworks extends Component {

  static contextTypes = {
    onSetTitle: PropTypes.func.isRequired,
  };

  static propTypes = {
    data: PropTypes.object,
    setWizardState: PropTypes.func
  }

  constructor(props) {
    super(props)

    this.networkOptions = null // [{ label: "Create new", value: null }]
    ConnectionsActions.loadNetworks(this.props.data.targetCloud.credential)

    props.data.selectedInstances.forEach((vm) => {
      ConnectionsActions.loadInstanceDetail({ id: this.props.data.sourceCloud.credential.id }, vm)
    })

    let valid = true
    if (props.data.networks) {
      props.data.networks.forEach(item => {
        if (item.migrateNetwork === null) {
          valid = false
        }
      })
    } else {
      valid = false
    }

    let networks = props.data.networks
    if (networks && networks.length) {
      networks.sort((a, b) => a.network_name > b.network_name)
    }

    this.state = {
      networks: networks || null,
      nextStep: "WizardSchedule",
      valid: valid
    }
  }

  componentWillMount() {
    WizardActions.updateWizardState(this.state)
    this.context.onSetTitle(title);
  }

  componentWillReceiveProps(props) {
    this.processProps(props)
  }

  processProps(props) {
    let networks = []

    props.data.selectedInstances.forEach((vm) => {
      if (vm.devices && vm.devices.nics) {
        vm.devices.nics.forEach((item) => {
          let exists = false
          networks.forEach(network => {
            if (network.network_name == item.network_name) {
              exists = true
            }
          })
          if (!exists) {
            if (!item.migrateNetwork) {
              item.migrateNetwork = null
            }
            networks.push(item)
          }
        })
      }
    })

    if (networks.length == 0) {
      networks = null
    }
    if (props.data.targetNetworks && props.data.targetNetworks.length) {
      this.networkOptions = []

      let targetNetworks = props.data.targetNetworks
      targetNetworks.sort((a, b) => a.name > b.name)

      targetNetworks.forEach((network) => {
        this.networkOptions.push({
          label: network.name,
          value: network.name
        })
      }, this)
    }
    this.setState({ networks: networks })
  }

  handleChangeNetwork(event, network) {
    let index = this.state.networks.indexOf(network)
    let valid = true
    let networks = this.state.networks
    networks[index].migrateNetwork = event.value
    networks.forEach(item => {
      if (item.migrateNetwork === null) {
        valid = false
      }
    })

    this.setState({
      networks: networks,
      valid: valid
    }, () => {
      WizardActions.updateWizardState(this.state)
    })
  }

  render() {
    if (this.state.networks != null) {
      let networks = this.state.networks.map((network, index) => {
        let networkDropdown
        if (this.networkOptions == null) {
          networkDropdown = "Searching for networks ..."
        } else if (this.networkOptions.length) {
          networkDropdown = (<Dropdown
            options={this.networkOptions}
            onChange={(e) => this.handleChangeNetwork(e, network)}
            value={network.migrateNetwork}
          />)
        } else {
          networkDropdown = "Could not find networks"
        }
        return (

                {network.network_name}

              {networkDropdown}

        )
      }, this)

      return (

              {networks}

      );
    } else {
      return (

              <LoadingIcon text="Loading networks..." />

      );
    }
  }

}

export default withStyles(WizardNetworks, s);
<|endoftext|>


<commit_before>import { isClassic } from '../../utils/helpers/style-helper';

/* eslint-disable key-spacing, no-multi-spaces */

const sizeParams = {
  'extra-small':  { dimensions: 24,  iconDimensions: 16 },
  small:          { dimensions: 32,  iconDimensions: 16 },
  'medium-small': { dimensions: 40,  iconDimensions: 24 },
  medium:         { dimensions: 56,  iconDimensions: 32 },
  'medium-large': { dimensions: 72,  iconDimensions: 32 },
  large:          { dimensions: 104, iconDimensions: 48 },
  'extra-large':  { dimensions: 128, iconDimensions: 64 }
};

const sizeParamsClassic = {
  'extra-small':  { dimensions: 25,  iconDimensions: null, iconPadding: 4  },
  small:          { dimensions: 30,  iconDimensions: null, iconPadding: 6  },
  'medium-small': { dimensions: 40,  iconDimensions: 24,   iconPadding: 12 },
  medium:         { dimensions: 60,  iconDimensions: 32,   iconPadding: 14 },
  'medium-large': { dimensions: 70,  iconDimensions: 32,   iconPadding: 18 },
  large:          { dimensions: 100, iconDimensions: 48,   iconPadding: 25 },
  'extra-large':  { dimensions: 120, iconDimensions: 64,   iconPadding: 27 }
};

/* eslint-enable key-spacing, no-multi-spaces */

export default function getSizeParams(theme, size) {
  return (isClassic(theme) ? sizeParamsClassic[size] : sizeParams[size]);
}
<commit_msg>(FE-1412) FE-1771: portrait-size.config.js: rename DLS sizes<commit_after>import { isClassic } from '../../utils/helpers/style-helper';

/* eslint-disable key-spacing, no-multi-spaces */

const sizeParams = {
  XS:  { dimensions: 24,  iconDimensions: 16 },
  S:   { dimensions: 32,  iconDimensions: 16 },
  M:   { dimensions: 40,  iconDimensions: 24 },
  ML:  { dimensions: 56,  iconDimensions: 32 },
  L:   { dimensions: 72,  iconDimensions: 32 },
  XL:  { dimensions: 104, iconDimensions: 48 },
  XXL: { dimensions: 128, iconDimensions: 64 }
};

const sizeParamsClassic = {
  'extra-small':  { dimensions: 25,  iconDimensions: null, iconPadding: 4  },
  small:          { dimensions: 30,  iconDimensions: null, iconPadding: 6  },
  'medium-small': { dimensions: 40,  iconDimensions: 24,   iconPadding: 12 },
  medium:         { dimensions: 60,  iconDimensions: 32,   iconPadding: 14 },
  'medium-large': { dimensions: 70,  iconDimensions: 32,   iconPadding: 18 },
  large:          { dimensions: 100, iconDimensions: 48,   iconPadding: 25 },
  'extra-large':  { dimensions: 120, iconDimensions: 64,   iconPadding: 27 }
};

/* eslint-enable key-spacing, no-multi-spaces */

export default function getSizeParams(theme, size) {
  return (isClassic(theme) ? sizeParamsClassic[size] : sizeParams[size]);
}
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';

import ErrorText from './ErrorText';
import HelpText from './HelpText';
import WarningText from './WarningText';

class ValidationText extends PureComponent {
  render() {
    const { error, inverse, help, warning } = this.props;

    if (error) {
      return <ErrorText inverse={inverse}>{error}</ErrorText>;
    }

    if (warning) {
      return <WarningText inverse={inverse}>{warning}</WarningText>;
    }

    if (help) {
      return <HelpText inverse={inverse}>{help}</HelpText>;
    }

    return null;
  }
}

ValidationText.propTypes = {
  error: PropTypes.node,
  help: PropTypes.node,
  inverse: PropTypes.bool,
  warning: PropTypes.node,
};

export default ValidationText;
<commit_msg>ValidationText: add 'warning' prop & implement WarningText<commit_after>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';

import ErrorText from './ErrorText';
import HelpText from './HelpText';
import WarningText from './WarningText';

class ValidationText extends PureComponent {
  render() {
    const { error, inverse, help, warning } = this.props;

    if (error) {
      return <ErrorText inverse={inverse}>{error}</ErrorText>
    }

    if (warning) {
      return <WarningText inverse={inverse}>{warning}</WarningText>
    }

    if (help) {
      return <HelpText inverse={inverse}>{help}</HelpText>
    }

    return null;
  }
}

ValidationText.propTypes = {
  error: PropTypes.node,
  help: PropTypes.node,
  inverse: PropTypes.bool,
  warning: PropTypes.node,
};

export default ValidationText;
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { View, StyleSheet, InteractionManager } from 'react-native';
import { connect } from 'react-redux';
import SearchHistory from '../components/SearchHistory';
import SearchUsersAutoCompleteList
  from '../components/SearchUsersAutoCompleteList';
import * as searchUsersAutoCompleteActionCreators
  from '../common/actions/searchUsersAutoComplete';
import { getSearchUsersAutoCompleteItems } from '../common/selectors';

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});

class SearchUsersAutoCompleteResult extends Component {
  componentDidMount() {
    const { word, clearSearchUsersAutoComplete } = this.props;
    InteractionManager.runAfterInteractions(() => {
      clearSearchUsersAutoComplete();
      this.submitSearchUsersAutoComplete(word);
    });
  }

  componentWillReceiveProps(nextProps) {
    const { word: prevWord } = this.props;
    const { word, clearSearchUsersAutoComplete } = nextProps;
    if (word && word !== prevWord) {
      InteractionManager.runAfterInteractions(() => {
        clearSearchUsersAutoComplete();
        this.submitSearchUsersAutoComplete(word);
      });
    }
  }

  loadMoreItems = () => {
    const {
      fetchSearchUsersAutoComplete,
      searchUsersAutoComplete: { nextUrl, loading },
    } = this.props;
    console.log('loading ', loading, nextUrl);
    if (!loading && nextUrl) {
      console.log('load more ', nextUrl);
      fetchSearchUsersAutoComplete(null, nextUrl);
    }
  };

  handleOnRefresh = () => {
    const {
      word,
      fetchSearchUsersAutoComplete,
      clearSearchUsersAutoComplete,
    } = this.props;
    clearSearchUsersAutoComplete();
    fetchSearchUsersAutoComplete(word, null, true);
  };

  submitSearchUsersAutoComplete = word => {
    const { fetchSearchUsersAutoComplete } = this.props;
    if (word && word.length > 1) {
      fetchSearchUsersAutoComplete(word);
    }
  };

  render() {
    const {
      searchUsersAutoComplete,
      word,
      searchUsersAutoComplete: { loading, loaded },
      items,
      searchHistory,
      onPressItem,
      onPressSearchHistoryItem,
      onPressRemoveSearchHistoryItem,
      onPressClearSearchHistory,
    } = this.props;
    return (
      <View style={styles.container}>
        {((!loaded && !loading) || !word) &&
          <SearchHistory
            items={searchHistory.items}
            onPressItem={onPressSearchHistoryItem}
            onPressRemoveSearchHistoryItem={onPressRemoveSearchHistoryItem}
            onPressClearSearchHistory={onPressClearSearchHistory}
          />}
        {word
          ? <SearchUsersAutoCompleteList
              data={{ ...searchUsersAutoComplete, items }}
              onPressItem={onPressItem}
              loadMoreItems={this.loadMoreItems}
              onRefresh={this.handleOnRefresh}
            />
          : null}
      </View>
    );
  }
}

export default connect(
  (state, props) => ({
    searchUsersAutoComplete: state.searchUsersAutoComplete,
    items: getSearchUsersAutoCompleteItems(state, props),
  }),
  searchUsersAutoCompleteActionCreators,
)(SearchUsersAutoCompleteResult);
<commit_msg>fixed search user auto complete duplicate key<commit_after>import React, { Component } from 'react';
import { View, StyleSheet, InteractionManager } from 'react-native';
import { connect } from 'react-redux';
import SearchHistory from '../components/SearchHistory';
import SearchUsersAutoCompleteList
  from '../components/SearchUsersAutoCompleteList';
import * as searchUsersAutoCompleteActionCreators
  from '../common/actions/searchUsersAutoComplete';
import { getSearchUsersAutoCompleteItems } from '../common/selectors';

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});

class SearchUsersAutoCompleteResult extends Component {
  componentDidMount() {
    const { word, clearSearchUsersAutoComplete } = this.props;
    clearSearchUsersAutoComplete();
    InteractionManager.runAfterInteractions(() => {
      this.submitSearchUsersAutoComplete(word);
    });
  }

  componentWillReceiveProps(nextProps) {
    const { word: prevWord } = this.props;
    const { word, clearSearchUsersAutoComplete } = nextProps;
    if (word && word !== prevWord) {
      clearSearchUsersAutoComplete();
      InteractionManager.runAfterInteractions(() => {
        this.submitSearchUsersAutoComplete(word);
      });
    }
  }

  loadMoreItems = () => {
    const {
      fetchSearchUsersAutoComplete,
      searchUsersAutoComplete: { nextUrl, loading },
    } = this.props;
    if (!loading && nextUrl) {
      console.log('load more ', nextUrl);
      fetchSearchUsersAutoComplete(null, nextUrl);
    }
  };

  handleOnRefresh = () => {
    const {
      word,
      fetchSearchUsersAutoComplete,
      clearSearchUsersAutoComplete,
    } = this.props;
    clearSearchUsersAutoComplete();
    fetchSearchUsersAutoComplete(word, null, true);
  };

  submitSearchUsersAutoComplete = word => {
    const { fetchSearchUsersAutoComplete } = this.props;
    if (word && word.length > 1) {
      fetchSearchUsersAutoComplete(word);
    }
  };

  render() {
    const {
      searchUsersAutoComplete,
      word,
      searchUsersAutoComplete: { loading, loaded },
      items,
      searchHistory,
      onPressItem,
      onPressSearchHistoryItem,
      onPressRemoveSearchHistoryItem,
      onPressClearSearchHistory,
    } = this.props;
    return (
      <View style={styles.container}>
        {((!loaded && !loading) || !word) &&
          <SearchHistory
            items={searchHistory.items}
            onPressItem={onPressSearchHistoryItem}
            onPressRemoveSearchHistoryItem={onPressRemoveSearchHistoryItem}
            onPressClearSearchHistory={onPressClearSearchHistory}
          />}
        {word
          ? <SearchUsersAutoCompleteList
              data={{ ...searchUsersAutoComplete, items }}
              onPressItem={onPressItem}
              loadMoreItems={this.loadMoreItems}
              onRefresh={this.handleOnRefresh}
            />
          : null}
      </View>
    );
  }
}

export default connect(
  (state, props) => ({
    searchUsersAutoComplete: state.searchUsersAutoComplete,
    items: getSearchUsersAutoCompleteItems(state, props),
  }),
  searchUsersAutoCompleteActionCreators,
)(SearchUsersAutoCompleteResult);
<|endoftext|>