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 ValueContainer
hijos 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
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>
);
}
}}
/>