Saltar al contenido

Cómo representar “N elementos seleccionados” en lugar de la lista de N elementos seleccionados con react-select

Solución:

Nota: esta respuesta es para react-select v1. Consulte la respuesta de NearHuscarl para obtener una solución para v3.

Procesando “N elementos seleccionados”

Esto se puede lograr con el valueRenderer y className accesorios y una cantidad mínima de CSS.

Aquí estoy mostrando las primeras tres selecciones normalmente, y luego “N elementos seleccionados” cuando se han seleccionado más de 4 elementos. No tiene sentido mostrar el eliminar selección icono (×) además de “N elementos seleccionados”, por lo que también eliminé eso (con CSS).

class App extends React.Component {
  state = {
    value: [],
  }
  className = () => {
    const baseClassName="my-react-select";
    
    if (this.state.value.length <= 3) {
      return baseClassName;
    }
    
    return `${baseClassName} ${baseClassName}--compact`;
  }
  handleChange = (value) => {
    this.setState({ value });
  }
  renderValue = (option) => {
    // The first three selections are rendered normally
    if (this.state.value.length <= 3) {
      return option.label;
    }

    // With more selections, render "N items selected".
    // Other than the first one are hidden in CSS.
    return <span>{this.state.value.length} items selected</span>;
  }
  render() {
    return (
      <Select
        className={this.className()}
        multi
        onChange={this.handleChange}
        options={[
          { value: 'zero',  label: 'Zero' },
          { value: 'one',   label: 'One' },
          { value: 'two',   label: 'Two' },
          { value: 'three', label: 'Three' },
          { value: 'four',  label: 'Four' },
          { value: 'five',  label: 'Five' },
          { value: 'six',   label: 'Six' },
          { value: 'seven', label: 'Seven' },
          { value: 'eight', label: 'Eight' },
          { value: 'nine',  label: 'Nine' },
        ]}
        value={this.state.value}
        valueRenderer={this.renderValue}
      />
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
.my-react-select {
  /* Custom styles */
}

.my-react-select--compact .Select-value:first-child {
  font-style: italic;
}
.my-react-select--compact .Select-value:first-child .Select-value-icon,
.my-react-select--compact .Select-value:nth-child(n+2) {
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<script src="https://unpkg.com/[email protected]/prop-types.js"></script>
<script src="https://unpkg.com/[email protected]/index.js"></script>
<script src="https://unpkg.com/[email protected]/dist/react-input-autosize.js"></script>
<script src="https://unpkg.com/[email protected]/dist/react-select.js"></script>

<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/react-select.css">

<div id="root"></div>


Enfoque alternativo

Al mirar sus capturas de pantalla, parece que hay espacio para mostrar hasta cuatro selecciones sin que el selector se desborde. En lugar de mostrar “N elementos seleccionados” cuando se han seleccionado más de 4 ciudades, podría mostrar las primeras 3 selecciones normalmente y luego “+ N más”. Como esto:

  • Ciudad A
  • Ciudad A, Ciudad B
  • Ciudad A, Ciudad B, Ciudad C
  • City A, City B, City C y 1 más
  • City A, City B, City C y 2 más
  • City A, City B, City C y 3 más
  • etc.

Desde la perspectiva de UX, creo que es bueno mostrar las primeras 3 o más selecciones normalmente. Es confuso si cada selección se oculta repentinamente detrás del texto “4 elementos seleccionados” después de seleccionar la cuarta ciudad.

Esta solución es muy similar a la primera. los className prop ahora es simplemente una cadena. los renderValue El método y los selectores de CSS son un poco diferentes.

class App extends React.Component {
  state = {
    value: [],
  }
  handleChange = (value) => {
    this.setState({ value });
  }
  renderValue = (option) => {
    // The first three values are rendered normally
    if (this.state.value.indexOf(option) < 3) {
      return option.label;
    }

    // Render the rest as "+ N more". 
    // Other than the first one are hidden in CSS.
    return <span>+ {this.state.value.length - 3} more</span>;
  }
  render() {
    return (
      <Select
        className="my-react-select"
        multi
        onChange={this.handleChange}
        options={[
          { value: 'zero',  label: 'Zero' },
          { value: 'one',   label: 'One' },
          { value: 'two',   label: 'Two' },
          { value: 'three', label: 'Three' },
          { value: 'four',  label: 'Four' },
          { value: 'five',  label: 'Five' },
          { value: 'six',   label: 'Six' },
          { value: 'seven', label: 'Seven' },
          { value: 'eight', label: 'Eight' },
          { value: 'nine',  label: 'Nine' },
        ]}
        value={this.state.value}
        valueRenderer={this.renderValue}
      />
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
/* If you change the amount of how many selections are shown normally,
 * be sure to adjust these selectors accordingly. */
.my-react-select .Select-value:nth-child(4) {
  font-style: italic;
}
.my-react-select .Select-value:nth-child(4) .Select-value-icon,
.my-react-select .Select-value:nth-child(n+5) {
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<script src="https://unpkg.com/[email protected]/prop-types.js"></script>
<script src="https://unpkg.com/[email protected]/index.js"></script>
<script src="https://unpkg.com/[email protected]/dist/react-input-autosize.js"></script>
<script src="https://unpkg.com/[email protected]/dist/react-select.js"></script>

<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/react-select.css">

<div id="root"></div>


Aquí hay otro enfoque para mostrar las selecciones:

  • Ciudad A
  • Ciudad A, Ciudad B
  • Ciudad A, Ciudad B, Ciudad C
  • Ciudad A, Ciudad B, Ciudad C, Ciudad D
  • City A, City B, City C y 2 más
  • City A, City B, City C y 3 más
  • etc.

Desde la perspectiva de UX, es un poco tonto mostrar “+ 1 más” en lugar de mostrar el valor, así que en mi opinión esta es la mejor opción.

los renderValue El método es una vez más un poco diferente. Los selectores de CSS ahora son un poco más feos y complejos, pero funcionan.

class App extends React.Component {
  state = {
    value: [],
  }
  handleChange = (value) => {
    this.setState({ value });
  }
  renderValue = (option) => {
    // The first four values are rendered normally
    if (this.state.value.length <= 4) {
      return option.label;
    }

    // The first 3 values are rendered normally when
    // more than 4 selections have been made
    if (this.state.value.indexOf(option) < 3) {
      return option.label;
    }

    // Render the rest as "+ N more".
    // Other than the first one are hidden in CSS.
    return <span>+ {this.state.value.length - 3} more</span>;
  }
  render() {
    return (
      <Select
        className="my-react-select"
        multi
        onChange={this.handleChange}
        options={[
          { value: 'zero',  label: 'Zero' },
          { value: 'one',   label: 'One' },
          { value: 'two',   label: 'Two' },
          { value: 'three', label: 'Three' },
          { value: 'four',  label: 'Four' },
          { value: 'five',  label: 'Five' },
          { value: 'six',   label: 'Six' },
          { value: 'seven', label: 'Seven' },
          { value: 'eight', label: 'Eight' },
          { value: 'nine',  label: 'Nine' },
        ]}
        value={this.state.value}
        valueRenderer={this.renderValue}
      />
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
/* If you change the amount of how many selections are shown normally,
 * be sure to adjust these selectors accordingly. */
.my-react-select .Select-value:nth-child(4):not(:nth-last-child(2)) {
  font-style: italic;
}
.my-react-select .Select-value:nth-child(4):not(:nth-last-child(2)) .Select-value-icon,
.my-react-select .Select-value:nth-child(n+5) {
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<script src="https://unpkg.com/[email protected]/prop-types.js"></script>
<script src="https://unpkg.com/[email protected]/index.js"></script>
<script src="https://unpkg.com/[email protected]/dist/react-input-autosize.js"></script>
<script src="https://unpkg.com/[email protected]/dist/react-select.js"></script>

<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/react-select.css">

<div id="root"></div>

Aquí está mi respuesta actualizada usando react-select 3.x. No hay CSS involucrado. Anulo el ValueContainerhijos con mis mensajes personalizados de valores múltiples. Antes de eso, necesita importar las siguientes cosas

import React from "react";
import Select, { components } from "react-select";

Variación 1

Mostrar mensaje genérico: n items selected

<Select
  ...
  isMulti
  closeMenuOnSelect={false}
  hideSelectedOptions={false}
  components={{
    ValueContainer: ({ children, ...props }) => {
      let [values, input] = children;

      if (Array.isArray(values)) {
        const plural = values.length === 1 ? "" : "s";
        values = `${values.length} item${plural} selected`;
      }

      return (
        <components.ValueContainer {...props}>
          {values}
          {input}
        </components.ValueContainer>
      );
    }
  }}
/>

Resultado

ingrese la descripción de la imagen aquí

Variación 2

Muestra varios elementos con nombre antes de mostrar el mensaje genérico: item1, item2, item3 and n others selected

<Select
  ...
  isMulti
  closeMenuOnSelect={false}
  hideSelectedOptions={false}
  components={{
    ValueContainer: ({ children, ...props }) => {
      let [values, input] = children;

      if (Array.isArray(values)) {
        const val = (i: number) => values[i].props.children;
        const { length } = values;

        // I know you can use loops here to create the message
        // but this keeps the logic simple and more maintainable in the long run.
        switch (length) {
          case 1:
            values = `${val(0)} selected`;
            break;
          case 2:
            values = `${val(0)} and ${val(1)} selected`;
            break;
          case 3:
            values = `${val(0)}, ${val(1)} and ${val(2)} selected`;
            break;
          default:
            const plural = values.length === 3 + 1 ? "" : "s";
            const otherCount = length - 3;
            values = `${val(0)}, ${val(1)}, ${val(
              2
            )} and ${otherCount} other${plural} selected`;
            break;
        }
      }

      return (
        <components.ValueContainer {...props}>
          {values}
          {input}
        </components.ValueContainer>
      );
    }
  }}
/>

ingrese la descripción de la imagen aquí

Demo en vivo

Editar mensajes personalizados de valor múltiple react-select

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *