ui: Add CSS ellipsis + Tooltip for SelectableButtonGroup; keep Tag intact

- Truncate long labels via pure CSS and always show full text in a Tooltip
- Ensure the right-side Tag is never truncated and remains fully visible
- Simplify implementation: remove overflow detection and ResizeObserver
- Use minimal markup with sbg-button/sbg-inner/sbg-label to enable shrinking
- Add global rules to allow `.semi-button-content` to shrink and ellipsize

Files:
- web/src/components/common/ui/SelectableButtonGroup.jsx
- web/src/index.css

No API changes; visuals improved and code complexity reduced.
This commit is contained in:
t0ng7u
2025-08-11 18:27:32 +08:00
parent 2d4edb3eca
commit e863be7ec3
2 changed files with 44 additions and 29 deletions

View File

@@ -17,10 +17,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import React, { useState, useRef } from 'react';
import React, { useState } from 'react';
import { useIsMobile } from '../../../hooks/common/useIsMobile';
import { useMinimumLoadingTime } from '../../../hooks/common/useMinimumLoadingTime';
import { Divider, Button, Tag, Row, Col, Collapsible, Checkbox, Skeleton } from '@douyinfe/semi-ui';
import { Divider, Button, Tag, Row, Col, Collapsible, Checkbox, Skeleton, Tooltip } from '@douyinfe/semi-ui';
import { IconChevronDown, IconChevronUp } from '@douyinfe/semi-icons';
/**
@@ -57,8 +57,6 @@ const SelectableButtonGroup = ({
const needCollapse = collapsible && items.length > perRow * maxVisibleRows;
const showSkeleton = useMinimumLoadingTime(loading);
const contentRef = useRef(null);
const maskStyle = isOpen
? {}
: {
@@ -131,7 +129,7 @@ const SelectableButtonGroup = ({
};
const contentElement = showSkeleton ? renderSkeletonButtons() : (
<Row gutter={[8, 8]} style={{ lineHeight: '32px', ...style }} ref={contentRef}>
<Row gutter={[8, 8]} style={{ lineHeight: '32px', ...style }}>
{items.map((item) => {
const isDisabled = item.disabled || (typeof item.tagCount === 'number' && item.tagCount === 0);
const isActive = Array.isArray(activeValue)
@@ -152,6 +150,7 @@ const SelectableButtonGroup = ({
theme={isActive ? 'light' : 'outline'}
type={isActive ? 'primary' : 'tertiary'}
disabled={isDisabled}
className="sbg-button"
icon={
<Checkbox
checked={isActive}
@@ -162,19 +161,15 @@ const SelectableButtonGroup = ({
}
style={{ width: '100%', cursor: 'default' }}
>
{item.icon && (
<span style={{ marginRight: 4 }}>{item.icon}</span>
)}
<span style={{ marginRight: item.tagCount !== undefined ? 4 : 0 }}>{item.label}</span>
<div className="sbg-content">
{item.icon && (<span className="sbg-icon">{item.icon}</span>)}
<Tooltip content={item.label}>
<span className="sbg-ellipsis">{item.label}</span>
</Tooltip>
{item.tagCount !== undefined && (
<Tag
color='white'
shape="circle"
size="small"
>
{item.tagCount}
</Tag>
<Tag className="sbg-tag" color='white' shape="circle" size="small">{item.tagCount}</Tag>
)}
</div>
</Button>
</Col>
);
@@ -192,20 +187,19 @@ const SelectableButtonGroup = ({
onClick={() => onChange(item.value)}
theme={isActive ? 'light' : 'outline'}
type={isActive ? 'primary' : 'tertiary'}
icon={item.icon}
disabled={isDisabled}
className="sbg-button"
style={{ width: '100%' }}
>
<span style={{ marginRight: item.tagCount !== undefined ? 4 : 0 }}>{item.label}</span>
<div className="sbg-content">
{item.icon && (<span className="sbg-icon">{item.icon}</span>)}
<Tooltip content={item.label}>
<span className="sbg-ellipsis">{item.label}</span>
</Tooltip>
{item.tagCount !== undefined && (
<Tag
color='white'
shape="circle"
size="small"
>
{item.tagCount}
</Tag>
<Tag className="sbg-tag" color='white' shape="circle" size="small">{item.tagCount}</Tag>
)}
</div>
</Button>
</Col>
);

View File

@@ -289,6 +289,27 @@ code {
}
/* ==================== 组件特定样式 ==================== */
/* SelectableButtonGroup */
.sbg-button .semi-button-content {
min-width: 0 !important;
}
.sbg-content {
display: flex;
align-items: center;
gap: 4px;
width: 100%;
min-width: 0;
}
.sbg-ellipsis {
flex: 1;
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/* Tabs组件样式 */
.semi-tabs-content {
padding: 0 !important;