({
children,
visibleChildCount,
overflowStyle = 'overlay',
sx: sxProp,
})
| 146 | |
| 147 | // TODO: reduce re-renders |
| 148 | const LabelGroup: React.FC<React.PropsWithChildren<LabelGroupProps>> = ({ |
| 149 | children, |
| 150 | visibleChildCount, |
| 151 | overflowStyle = 'overlay', |
| 152 | sx: sxProp, |
| 153 | }) => { |
| 154 | const containerRef = React.useRef<HTMLDivElement>(null) |
| 155 | const collapseButtonRef = React.useRef<HTMLButtonElement>(null) |
| 156 | const firstHiddenIndexRef = React.useRef<number | undefined>(undefined) |
| 157 | const [visibilityMap, setVisibilityMap] = React.useState<Record<string, boolean>>({}) |
| 158 | const [isOverflowShown, setIsOverflowShown] = React.useState<boolean>(false) |
| 159 | const [buttonClientRect, setButtonClientRect] = React.useState<DOMRect>({ |
| 160 | width: 0, |
| 161 | right: 0, |
| 162 | height: 0, |
| 163 | x: 0, |
| 164 | y: 0, |
| 165 | top: 0, |
| 166 | left: 0, |
| 167 | bottom: 0, |
| 168 | toJSON: () => undefined, |
| 169 | }) |
| 170 | |
| 171 | const {theme} = useTheme() |
| 172 | |
| 173 | const overlayPaddingPx = parseInt(get('space.2')(theme), 10) |
| 174 | |
| 175 | const hiddenItemIds = Object.keys(visibilityMap).filter(key => !visibilityMap[key]) |
| 176 | |
| 177 | // `overlayWidth` is only needed when we render an overlay |
| 178 | // if we don't use an overlay, we can skip the width calculation |
| 179 | // and save on reflows caused by measuring DOM nodes. |
| 180 | const overlayWidth = |
| 181 | hiddenItemIds.length && overflowStyle === 'overlay' |
| 182 | ? getOverlayWidth(buttonClientRect, containerRef, overlayPaddingPx) |
| 183 | : undefined |
| 184 | |
| 185 | const expandButtonRef: React.RefCallback<HTMLButtonElement> = React.useCallback( |
| 186 | node => { |
| 187 | if (node !== null) { |
| 188 | const nodeClientRect = node.getBoundingClientRect() |
| 189 | |
| 190 | if (nodeClientRect.width !== buttonClientRect.width || nodeClientRect.right !== buttonClientRect.right) { |
| 191 | setButtonClientRect(nodeClientRect) |
| 192 | } |
| 193 | |
| 194 | // @ts-ignore you can set `.current` on ref objects or ref callbacks in React |
| 195 | expandButtonRef.current = node |
| 196 | } |
| 197 | }, |
| 198 | [buttonClientRect], |
| 199 | ) |
| 200 | |
| 201 | // Sets the visibility map to hide children after the given index. |
| 202 | const hideChildrenAfterIndex = React.useCallback((truncateAfter: number) => { |
| 203 | const containerChildren = containerRef.current?.children || [] |
| 204 | const updatedEntries: Record<string, boolean> = {} |
| 205 | for (const child of containerChildren) { |
nothing calls this directly
no test coverage detected