-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
187951404 two line attribute header (#1514)
* Splits header into two lines. There is a bug with editing attribute header text. * Fixes bug where header doesn't update after rename * Adds logic so that line 1 always shows 1 word Adds css styling so line doesn't wrap word, and line 2 shows ellipsis on left side. * fix merge conflict error * Fixes broken cypress test in table * Fixes logic for showing attribute units as undefined if there are no units * Adds padding buffer to button width Adds logic that if there is only one attribute name and it overflows, don't make it appear in the 2nd line. * Adds wrap styling for one word headers * PR fixes * Adds code to styling of attribute header line 2
- Loading branch information
Showing
5 changed files
with
166 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import { useEffect, useRef, useState } from 'react' | ||
import { measureText } from './use-measure-text' | ||
|
||
const kPaddingBuffer = 5 // Button width is 5px smaller because of parent padding | ||
|
||
// Hook to split headers into 2 rows and elide the 2nd line if it doesn't fit | ||
export function useAdjustHeaderForOverflow(attrbuteHeaderButtonEl: HTMLButtonElement | null, | ||
attrName: string, attrUnits?: string) { | ||
const attributeName = attrName.replace(/_/g, ' ') | ||
const candidateAttributeLabel = `${attributeName}${attrUnits}`.trim() | ||
const [line1, setLine1] = useState('') | ||
const [line2, setLine2] = useState('') | ||
const [isOverflowed, setIsOverflowed] = useState(false) | ||
const [line2Truncated, setLine2Truncated] = useState(false) | ||
const resizeObserverRef = useRef<ResizeObserver | null>(null) | ||
|
||
const reverse = (str: string) => { | ||
const rMap: Record<string, string> = {'[': ']', ']': '[', '{': '}', '}': '{', '(': ')', ')': '('} | ||
if (!str) return str | ||
const s = str.split('') | ||
let c = "" | ||
const n = [] | ||
for (let ix = s.length - 1; ix >= 0; ix -= 1) { | ||
c = s[ix] | ||
if (rMap[c]) c = rMap[c] | ||
n.push(c) | ||
} | ||
return n.join('') | ||
} | ||
|
||
const calculateSplit = () => { | ||
if (!attrbuteHeaderButtonEl) { | ||
setLine1('') | ||
setLine2('') | ||
setIsOverflowed(false) | ||
return | ||
} | ||
|
||
const attributeButtonWidth = attrbuteHeaderButtonEl.clientWidth - kPaddingBuffer | ||
const computedStyle = getComputedStyle(attrbuteHeaderButtonEl) | ||
const style = [ | ||
`font-style:${computedStyle.fontStyle}`, | ||
`font-variant:${computedStyle.fontVariant}`, | ||
`font-weight:${computedStyle.fontWeight}`, | ||
`font-size:${computedStyle.fontSize}`, | ||
`font-family:${computedStyle.fontFamily}`, | ||
`width:${attrbuteHeaderButtonEl.clientWidth}px`, | ||
`height:${attrbuteHeaderButtonEl.clientHeight}px` | ||
].join('') | ||
const fullTextWidth = measureText(candidateAttributeLabel, style) | ||
const words = candidateAttributeLabel.split(' ') | ||
if (fullTextWidth <= attributeButtonWidth || words.length === 1) { | ||
setLine1(candidateAttributeLabel) | ||
setLine2('') | ||
setIsOverflowed(false) | ||
} else { | ||
let i = 0 | ||
let currentLine1 = '' | ||
// Build line1 word by word without exceeding the button width | ||
while (i < words.length && measureText(currentLine1 + words[i], style) < attributeButtonWidth) { | ||
currentLine1 = currentLine1 ? `${currentLine1} ${words[i]}` : words[i] | ||
i++ | ||
} | ||
// If line1 ends up with just one word, show the word no matter what the width is | ||
if (currentLine1.split(' ').length === 1) { | ||
currentLine1 = words[0] | ||
} | ||
setLine1(currentLine1) | ||
|
||
const remainingWords = words.slice(i).join(' ') | ||
const remainingTextWidth = measureText(remainingWords, style) | ||
if (remainingTextWidth <= attributeButtonWidth) { | ||
// Remaining text fits in line2 | ||
setLine2(remainingWords) | ||
setLine2Truncated(false) | ||
} else { | ||
// Remaining text doesn't fit in line2 | ||
// We reverse the text so that the ellipsis is at the beginning of the text and | ||
// line2 has style of direction: rtl, text-align: left, unicode-bidi: bidi-override | ||
setLine2Truncated(true) | ||
setLine2(reverse(candidateAttributeLabel)) | ||
} | ||
setIsOverflowed(true) | ||
} | ||
} | ||
|
||
useEffect(() => { | ||
if (!attrbuteHeaderButtonEl) return | ||
resizeObserverRef.current = new ResizeObserver(() => { | ||
calculateSplit() | ||
}) | ||
resizeObserverRef.current.observe(attrbuteHeaderButtonEl) | ||
return () => { | ||
resizeObserverRef.current?.disconnect() | ||
} | ||
// Adding calculateSplit to dependencies causes rerender problems on attribute rename | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [candidateAttributeLabel, attrbuteHeaderButtonEl]) | ||
|
||
useEffect(()=> { | ||
calculateSplit() | ||
// Adding calculateSplit to dependencies causes rerender problems on attribute rename | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [candidateAttributeLabel]) | ||
|
||
return { line1, line2, isOverflowed, line2Truncated } | ||
} |