skip to Main Content

I have Autocomplete to select multiple options from React MUI. When out of focus, I want it to show only that amount of tags, that fits into one row. I can use limitTags={3}, to limit the number of tags.

But There are two issues:

  • If the input is shorter(on smaller screen) I need to show only 1 or 2 or some other number calculated by the length of the Autocomplete.
  • If the selected option titles are too long, than show just one for example. And if they are short, show maybe even more than 3. This should be calculated by length of shown tag titles.

Is this possible?

I am using example code:

      <Autocomplete
        multiple
        id="tags-standard"
        options={top100Films}
        getOptionLabel={(option) => option.title}
        limitTags={3}
        disableCloseOnSelect
        defaultValue={[top100Films[13]]}
        renderInput={(params) => (
          <TextField
            {...params}
            variant="standard"
            label="Multiple values"
            placeholder="Favorites"
          />
        )}
      />

2

Answers


  1. For such purpose you have the property called renderTags. Docs: https://mui.com/material-ui/api/autocomplete/#autocomplete-prop-renderTags

    You can try something like this (omitting other Autocomplete values for brevity). -1 for limitTags is the default value.

    <Autocomplete
        limitTags={-1}
        renderTags={(value: Value[]) => {
           // Value is the array of selected options
           // Your logic what to show in the input
           // You can use window.innerWidth to get the available size
           return value.map((o, i) => <Chip label={o.title} />);
       }}
    />
    
    Login or Signup to reply.
  2. This requires listening window resize event, Autocomplete value change event as well as calculating the widths of selected items by using ref.

    You can use this code to handle all those requirements:

    function useWindowWidth() {
      const [width, setWidth] = React.useState(0);
      React.useLayoutEffect(() => {
        function updateSize() {
          setWidth(window.innerWidth);
        }
        window.addEventListener('resize', updateSize);
        updateSize();
        return () => window.removeEventListener('resize', updateSize);
      }, []);
      return width;
    }
    
    export default function ResponsiveLimitTags() {
      const autocompleteRef = React.useRef();
      const width = useWindowWidth();
      const [resizeCompleted, setResizeCompleted] = React.useState(false);
      const [responsiveLimitTags, setResponsiveLimitTags] = React.useState(999999); // something really big
    
      const [autocompleteValue, setAutocompleteValue] = React.useState([
        top100Films[13],
        top100Films[12],
        top100Films[11],
      ]);
    
      React.useEffect(() => {
        setResizeCompleted(false);
        setResponsiveLimitTags(999999);
        const timer1 = setTimeout(() => {
          setResizeCompleted(true);
        }, 200);
        return () => {
          clearTimeout(timer1);
        };
      }, [width]);
    
      React.useEffect(() => {
        if (autocompleteRef.current && resizeCompleted) {
          const chips = autocompleteRef.current.querySelectorAll('.MuiChip-root');
          let yOfFirstRow = -1;
          let indexToBeBreaked = -1;
          for (let i = 0; i < chips.length; i++) {
            const currentChip = chips[i];
            const yOfCurrentChip = currentChip.getClientRects()[0]
              ? currentChip.getClientRects()[0].y
              : null;
            if (yOfCurrentChip) {
              if (yOfFirstRow < 0) {
                yOfFirstRow = yOfCurrentChip;
              }
              if (yOfCurrentChip > yOfFirstRow) {
                indexToBeBreaked = i;
                break;
              }
            }
          }
          if (indexToBeBreaked != responsiveLimitTags) {
            setResponsiveLimitTags(indexToBeBreaked);
          }
        }
      }, [autocompleteRef.current, autocompleteValue, resizeCompleted]);
    
      return (
        <Autocomplete
          ref={autocompleteRef}
          multiple
          limitTags={responsiveLimitTags}
          id="multiple-limit-tags"
          options={top100Films}
          getOptionLabel={(option) => option.title}
          value={autocompleteValue}
          onChange={(event, newValues) => setAutocompleteValue(newValues)}
          renderInput={(params) => (
            <TextField
              {...params}
              label="responsive limitTags"
              placeholder="Favorites"
              sx={{
                width: '95vw',
              }}
            />
          )}
        />
      );
    }
    

    You can take a look at this StackBlitz for a live working example of this answer.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search