I have a material ui autocomplete like this
I want to calculate that the fourth chip wrapped to next line so that I can make further calculations. Is there a way to do that in javascript/react with this autocomplete of material UI.
So far the documentation of autocomplete material ui is not helping in this case.
Basically what I have to do is to make the number of limitTags dynamic so that on changing the width of the autocomplete the limitTags is set accordingly and is not a static number.
So far my idea was to calculate the width of each tag, add it altogether and compare with the container width. That has been done but it still is buggy because as you can see in the image attached even though the combined length of the tags is less than the container width, the limitTags is 4 but it should be 3 because the last one wrapped to the next line. And now I'm stuck again. How can I detect which of the very first tag wrapped to the next line in order to calculate the limitTags value
Here's what I have done so far
const DEFAULT_LIMIT_TAGS = -1;
const useLimitTags = (value, autoCompleteContainerRef, tagRef) => {
const [limitTags, setLimitTags] = useState(DEFAULT_LIMIT_TAGS);
const [autoCompleteContainerWidth, setAutoCompleteContainerWidth] = useState(0);
const parentNode = useRef(null);
useOnResize(autoCompleteContainerRef, setAutoCompleteContainerWidth);
useLayoutEffect(() => {
if (!parentNode.current && tagRef?.current) {
parentNode.current = tagRef.current.parentNode;
}
if (parentNode?.current) {
const children = [...parentNode.current.children];
const tags = children.slice(0, value.length);
const endAdornmentWidth = children[children.length - 2].offsetWidth;
const containerWidth = autoCompleteContainerWidth - endAdornmentWidth - 20; // 20 is the margin around the last closing icon and the dropd own icon
const tagsCombinedWidth = tags.reduce((sum, child, index, arr) => {
const tagsWidth = sum + child.offsetWidth;
if (tagsWidth > containerWidth) {
setLimitTags(index - 1);
arr.splice(0, index + 1); // break
}
return tagsWidth;
}, 0);
if (tagsCombinedWidth <= containerWidth) {
setLimitTags(DEFAULT_LIMIT_TAGS);
}
}
}, [value, autoCompleteContainerWidth, tagRef]);
return limitTags;
};
And useOnResize is using Resize Observer API to calculate width of the changing autocomplete container
import { useEffect } from 'react';
import isFunction from 'lodash-es/isFunction';
const useOnResize = (componentRef, callback) => {
const shouldMountObserver = isFunction(callback) && componentRef?.current;
const resizeObserver =
shouldMountObserver &&
new ResizeObserver((entries) => {
for (const entry of entries) {
const { width: detectedWidth } = entry.contentRect;
callback(detectedWidth);
}
});
useEffect(() => {
const savedRef = componentRef?.current;
shouldMountObserver && resizeObserver?.observe(componentRef.current);
return () => {
if (savedRef) {
resizeObserver?.unobserve(savedRef);
}
}; // Resized observer omitted in deps array on purpose as it is not supposed to change between rerender
}, [componentRef?.current]); // eslint-disable-line react-hooks/exhaustive-deps
};
export default useOnResize;
