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 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 { useIsMobile } from '../../../hooks/common/useIsMobile';
import { useMinimumLoadingTime } from '../../../hooks/common/useMinimumLoadingTime'; 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'; import { IconChevronDown, IconChevronUp } from '@douyinfe/semi-icons';
/** /**
@@ -57,8 +57,6 @@ const SelectableButtonGroup = ({
const needCollapse = collapsible && items.length > perRow * maxVisibleRows; const needCollapse = collapsible && items.length > perRow * maxVisibleRows;
const showSkeleton = useMinimumLoadingTime(loading); const showSkeleton = useMinimumLoadingTime(loading);
const contentRef = useRef(null);
const maskStyle = isOpen const maskStyle = isOpen
? {} ? {}
: { : {
@@ -131,7 +129,7 @@ const SelectableButtonGroup = ({
}; };
const contentElement = showSkeleton ? renderSkeletonButtons() : ( 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) => { {items.map((item) => {
const isDisabled = item.disabled || (typeof item.tagCount === 'number' && item.tagCount === 0); const isDisabled = item.disabled || (typeof item.tagCount === 'number' && item.tagCount === 0);
const isActive = Array.isArray(activeValue) const isActive = Array.isArray(activeValue)
@@ -152,6 +150,7 @@ const SelectableButtonGroup = ({
theme={isActive ? 'light' : 'outline'} theme={isActive ? 'light' : 'outline'}
type={isActive ? 'primary' : 'tertiary'} type={isActive ? 'primary' : 'tertiary'}
disabled={isDisabled} disabled={isDisabled}
className="sbg-button"
icon={ icon={
<Checkbox <Checkbox
checked={isActive} checked={isActive}
@@ -162,19 +161,15 @@ const SelectableButtonGroup = ({
} }
style={{ width: '100%', cursor: 'default' }} style={{ width: '100%', cursor: 'default' }}
> >
{item.icon && ( <div className="sbg-content">
<span style={{ marginRight: 4 }}>{item.icon}</span> {item.icon && (<span className="sbg-icon">{item.icon}</span>)}
)} <Tooltip content={item.label}>
<span style={{ marginRight: item.tagCount !== undefined ? 4 : 0 }}>{item.label}</span> <span className="sbg-ellipsis">{item.label}</span>
{item.tagCount !== undefined && ( </Tooltip>
<Tag {item.tagCount !== undefined && (
color='white' <Tag className="sbg-tag" color='white' shape="circle" size="small">{item.tagCount}</Tag>
shape="circle" )}
size="small" </div>
>
{item.tagCount}
</Tag>
)}
</Button> </Button>
</Col> </Col>
); );
@@ -192,20 +187,19 @@ const SelectableButtonGroup = ({
onClick={() => onChange(item.value)} onClick={() => onChange(item.value)}
theme={isActive ? 'light' : 'outline'} theme={isActive ? 'light' : 'outline'}
type={isActive ? 'primary' : 'tertiary'} type={isActive ? 'primary' : 'tertiary'}
icon={item.icon}
disabled={isDisabled} disabled={isDisabled}
className="sbg-button"
style={{ width: '100%' }} style={{ width: '100%' }}
> >
<span style={{ marginRight: item.tagCount !== undefined ? 4 : 0 }}>{item.label}</span> <div className="sbg-content">
{item.tagCount !== undefined && ( {item.icon && (<span className="sbg-icon">{item.icon}</span>)}
<Tag <Tooltip content={item.label}>
color='white' <span className="sbg-ellipsis">{item.label}</span>
shape="circle" </Tooltip>
size="small" {item.tagCount !== undefined && (
> <Tag className="sbg-tag" color='white' shape="circle" size="small">{item.tagCount}</Tag>
{item.tagCount} )}
</Tag> </div>
)}
</Button> </Button>
</Col> </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组件样式 */ /* Tabs组件样式 */
.semi-tabs-content { .semi-tabs-content {
padding: 0 !important; padding: 0 !important;