React custom components - necrifede/my-dev-setup GitHub Wiki

Here are my react custom components

uiverse.io

uiverse.io

Simple Multi Select

based on custom dropdown

The visual structure of a dropdown menu component basic structure

HTML could look like this:

<div className="dd-wrapper">
  <div className="dd-header">
    <div className="dd-header-title"></div>
  </div>
  <div className="dd-list">
    <button className="dd-list-item"></button>
    <button className="dd-list-item"></button>
    <button className="dd-list-item"></button>
  </div>
</div>

Parent-child relations in components

constructor(){
  super()
  this.state = {
    location: [
      {
          id: 0,
          title: 'New York',
          selected: false,
          key: 'location'
      },
      {
          id: 1,
          title: 'Dublin',
          selected: false,
          key: 'location'
      },
      {
          id: 2,
          title: 'California',
          selected: false,
          key: 'location'
      },
      {
          id: 3,
          title: 'Istanbul',
          selected: false,
          key: 'location'
      },
      {
          id: 4,
          title: 'Izmir',
          selected: false,
          key: 'location'
      },
      {
          id: 5,
          title: 'Oslo',
          selected: false,
          key: 'location'
      }
    ]
   }
  }
<Dropdown  
  title="Select location"
  list={this.state.location}
/>
<constructor(props){
  super(props)
  this.state = {
    isListOpen: false,
    headerTitle: this.props.title
  }
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
render() {
  const { isListOpen, headerTitle } = this.state;
  const { list } = this.props;

  return (
    <div className="dd-wrapper">
      <button
        type="button"
        className="dd-header"
        onClick={this.toggleList}
      >
        <div className="dd-header-title">{headerTitle}</div>
        {isListOpen
          ? <FontAwesome name="angle-up" size="2x" />
          : <FontAwesome name="angle-down" size="2x" />}
      </button>
      {isListOpen && (
        <div
          role="list"
          className="dd-list"
        >
          {list.map((item) => (
            <button
              type="button"
              className="dd-list-item"
              key={item.id}
              onClick={() => this.selectItem(item)}
            >
              {item.title}
              {' '}
              {item.selected && <FontAwesome name="check" />}
            </button>
          ))}
        </div>
      )}
    </div>
  )
}
toggleList = () => {
   this.setState(prevState => ({
     isListOpen: !prevState.isListOpen
  }))
}
selectItem = (item) => {
  const { resetThenSet } = this.props;
  const { title, id, key } = item;

  this.setState({
    headerTitle: title,
    isListOpen: false,
  }, () => resetThenSet(id, key));
}

Controlling a parent state from a child component

resetThenSet = (id, key) => {
  const temp = [...this.state[key]];

  temp.forEach((item) => item.selected = false);
  temp[id].selected = true;

  this.setState({
    [key]: temp,
  });
}
<Dropdown
  title="Select location"
  list={this.state.location}
  resetThenSet={this.resetThenSet}
/>

Single- or multi-select dropdown

toggleItem = (id, key) => {
  const temp = [...this.state[key]];

  temp[id].selected = !temp[id].selected;

  this.setState({
    [key]: temp,
  });
}
<Dropdown
  title="Select location"
  list={this.state.location}
  toggleItem={this.toggleItem}
/>
render() {
  const { list, toggleItem } = this.props;

  return (
    //
    //
      <button
        type="button"
        className="dd-list-item"
        key={item.id}
        onClick={() => toggleItem(item.id, item.key)}
      >
    //
    //
  )
}

Dynamic header title

static getDerivedStateFromProps(nextProps) {
  const { list, title } = nextProps;
  const selectedItem = list.filter((item) => item.selected);

  if (selectedItem.length) {
    return {
      headerTitle: selectedItem[0].title,
    };
  }
  return { headerTitle: title };
}

Multi-select dropdown menu

static getDerivedStateFromProps(nextProps) {
  const {
    list,
    title,
    titleHelper,
    titleHelperPlural
  } = nextProps;

  const count = list.filter((item) => item.selected).length;

  if (count === 0) {
    return { headerTitle: title };
  }
  if (count === 1) {
    return { headerTitle: `${count} ${titleHelper}` };
  }
  if (count > 1) {
    return { headerTitle: `${count} ${titleHelperPlural}` };
  }
  return null;
}
<Dropdown
  titleHelper="Location"
  titleHelperPlural="Locations"
  title="Select location"
  list={this.state.location}
  toggleItem={this.toggleItem}
/>

Handling outside clicks

close = () => {
  this.setState({
    isListOpen: false,
  });
}

componentDidUpdate(){
  const { isListOpen } = this.state;

  if(isOpen){
    window.addEventListener('click', this.close)
  }
  else{
    window.removeEventListener('click', this.close)
  }
}
componentDidUpdate(){
  const { isListOpen } = this.state;

  setTimeout(() => {
    if(isListOpen){
      window.addEventListener('click', this.close)
    }
    else{
      window.removeEventListener('click', this.close)
    }
  }, 0)
}
<button
  type="button"
  className="dd-list-item"
  key={item.id}
  onClick={(e) => {
    e.stopPropagation();
    this.selectItem(item);
  }}
>
⚠️ **GitHub.com Fallback** ⚠️