Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix EntityDisplay text truncation #1299

Merged
merged 1 commit into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 66 additions & 41 deletions packages/stateless/components/EntityDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable i18next/no-literal-string */
import { fromBech32 } from '@cosmjs/encoding'
import { Check, CopyAllOutlined } from '@mui/icons-material'
import clsx from 'clsx'
import { useEffect, useState } from 'react'
Expand All @@ -13,7 +14,7 @@ import {
toBech32Hash,
} from '@dao-dao/utils'

import { useDaoNavHelpers } from '../hooks'
import { useDaoNavHelpers, useDetectTruncate } from '../hooks'
import { ButtonLink } from './buttons'
import { IconButton } from './icon_buttons'
import { Tooltip } from './tooltip/Tooltip'
Expand Down Expand Up @@ -49,58 +50,82 @@ export const EntityDisplay = ({
? getDaoPath(address)
: WALLET_URL_PREFIX + address

const { textRef, truncated } = useDetectTruncate()

// Use bech32 prefix length to determine how much to truncate from beginning.
let prefixLength
try {
prefixLength = fromBech32(address).prefix.length
} catch (e) {
// Conservative estimate.
prefixLength = 6
}

// If name exists, use it. Otherwise, fallback to address, potentially
// truncated.
const textDisplay =
!loadingEntity.loading && loadingEntity.data.name
? loadingEntity.data.name
: showFullAddress
? address
: concatAddressStartEnd(address, prefixLength + 3, 3)

return (
<div
className={clsx('flex min-w-0 flex-row items-center gap-2', className)}
>
<ButtonLink
className={clsx(loadingEntity.loading && 'animate-pulse')}
href={href}
openInNewTab
variant={noUnderline ? 'none' : 'underline'}
<Tooltip
title={
// Show text display tooltip if text is truncated.
truncated ? textDisplay : undefined
}
>
{!hideImage && (
<div
className="shrink-0 rounded-full bg-cover bg-center"
style={{
backgroundImage: `url(${
loadingEntity.loading
? getFallbackImage(toBech32Hash(address))
: toAccessibleImageUrl(loadingEntity.data.imageUrl)
})`,
width: imageSize,
height: imageSize,
}}
></div>
)}

<p
className={clsx(
{
'text-sm': size === 'default',
'text-lg': size === 'lg',
},
textClassName
)}
<ButtonLink
className={clsx(loadingEntity.loading && 'animate-pulse')}
containerClassName="min-w-0"
href={href}
onClick={(e) => e.stopPropagation()}
openInNewTab
variant={noUnderline ? 'none' : 'underline'}
>
{
// If name exists, use it. Otherwise, fall back to address,
// potentially truncated.
!loadingEntity.loading && loadingEntity.data.name
? loadingEntity.data.name
: showFullAddress
? address
: concatAddressStartEnd(address, 6, 4)
}
</p>
</ButtonLink>
{!hideImage && (
<div
className="shrink-0 rounded-full bg-cover bg-center"
style={{
backgroundImage: `url(${
loadingEntity.loading
? getFallbackImage(toBech32Hash(address))
: toAccessibleImageUrl(loadingEntity.data.imageUrl)
})`,
width: imageSize,
height: imageSize,
}}
></div>
)}

<p
className={clsx(
'min-w-0 truncate',
{
'text-sm': size === 'default',
'text-lg': size === 'lg',
},
textClassName
)}
ref={textRef}
>
{textDisplay}
</p>
</ButtonLink>
</Tooltip>

{!noCopy && (
<Tooltip title={t('button.copyAddress')}>
<IconButton
Icon={copied ? Check : CopyAllOutlined}
iconClassName="text-icon-tertiary"
onClick={() => {
onClick={(e) => {
e.stopPropagation()
navigator.clipboard.writeText(address)
setCopied(true)
}}
Expand Down
1 change: 1 addition & 0 deletions packages/stateless/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './useButtonPopupSorter'
export * from './useCachedLoadable'
export * from './useDaoInfoContext'
export * from './useDaoNavHelpers'
export * from './useDetectTruncate'
export * from './useDetectWrap'
export * from './useMountedInBrowser'
export * from './usePlatform'
Expand Down
63 changes: 63 additions & 0 deletions packages/stateless/hooks/useDetectTruncate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { RefCallback, useCallback, useEffect, useRef, useState } from 'react'

export type UseDetectTruncateReturn = {
textRef: RefCallback<HTMLParagraphElement | null>
truncated: boolean
}

// This hook detects when text is truncated.
//
// To use it, simply call this hook, and then assign the `textRef` to the
// appropriate element. The `truncated` value will be updated when the text is
// truncated.
export const useDetectTruncate = (): UseDetectTruncateReturn => {
const [truncated, setTruncated] = useState(false)

// Detect when the recipient wraps to the next page to change arrow.
const [refSet, setRefSet] = useState(0)
const _textRef = useRef<HTMLParagraphElement | null>(null)
const textRef: RefCallback<HTMLParagraphElement> = useCallback((node) => {
_textRef.current = node
setRefSet((refSet) => refSet + 1)
}, [])

useEffect(() => {
if (typeof window === 'undefined') {
return
}

const onResize = () => {
if (!_textRef.current) {
return
}

const { offsetWidth, scrollWidth } = _textRef.current

setTruncated(scrollWidth > offsetWidth)
}

// Trigger initial set.
onResize()

// Observe changes to the container and child elements.
const observer = new ResizeObserver(onResize)
if (_textRef.current) {
observer.observe(_textRef.current)
}

// Trigger re-render when window is resized.
window.addEventListener('resize', onResize)

return () => {
observer.disconnect()
window.removeEventListener('resize', onResize)
}

// Trigger re-render when refs are set.
}, [refSet])

return {
textRef,
truncated,
}
}
1 change: 1 addition & 0 deletions packages/stateless/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"lint": "eslint ."
},
"dependencies": {
"@cosmjs/encoding": "^0.30.1",
"@dao-dao/types": "2.2.0",
"@dao-dao/utils": "2.2.0",
"@emotion/react": "^11.10.4",
Expand Down