feat: enhance PricingTags and SelectableButtonGroup with new badge styles and color variants
Some checks failed
Publish Docker image (Multi Registries, native amd64+arm64) / Build & push (amd64) [native] (push) Has been cancelled
Publish Docker image (Multi Registries, native amd64+arm64) / Build & push (arm64) [native] (push) Has been cancelled
Publish Docker image (Multi Registries, native amd64+arm64) / Create multi-arch manifests (Docker Hub) (push) Has been cancelled
Some checks failed
Publish Docker image (Multi Registries, native amd64+arm64) / Build & push (amd64) [native] (push) Has been cancelled
Publish Docker image (Multi Registries, native amd64+arm64) / Build & push (arm64) [native] (push) Has been cancelled
Publish Docker image (Multi Registries, native amd64+arm64) / Create multi-arch manifests (Docker Hub) (push) Has been cancelled
This commit is contained in:
parent
6f818574ab
commit
bd6b728622
@ -23,7 +23,6 @@ import { useContainerWidth } from '../../../hooks/common/useContainerWidth';
|
|||||||
import {
|
import {
|
||||||
Divider,
|
Divider,
|
||||||
Button,
|
Button,
|
||||||
Tag,
|
|
||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
Collapsible,
|
Collapsible,
|
||||||
@ -46,6 +45,7 @@ import { IconChevronDown, IconChevronUp } from '@douyinfe/semi-icons';
|
|||||||
* @param {number} collapseHeight 折叠时的高度,默认200
|
* @param {number} collapseHeight 折叠时的高度,默认200
|
||||||
* @param {boolean} withCheckbox 是否启用前缀 Checkbox 来控制激活状态
|
* @param {boolean} withCheckbox 是否启用前缀 Checkbox 来控制激活状态
|
||||||
* @param {boolean} loading 是否处于加载状态
|
* @param {boolean} loading 是否处于加载状态
|
||||||
|
* @param {string} variant 颜色变体: 'violet' | 'teal' | 'amber' | 'rose' | 'green',不传则使用默认蓝色
|
||||||
*/
|
*/
|
||||||
const SelectableButtonGroup = ({
|
const SelectableButtonGroup = ({
|
||||||
title,
|
title,
|
||||||
@ -58,6 +58,7 @@ const SelectableButtonGroup = ({
|
|||||||
collapseHeight = 200,
|
collapseHeight = 200,
|
||||||
withCheckbox = false,
|
withCheckbox = false,
|
||||||
loading = false,
|
loading = false,
|
||||||
|
variant,
|
||||||
}) => {
|
}) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [skeletonCount] = useState(12);
|
const [skeletonCount] = useState(12);
|
||||||
@ -178,9 +179,6 @@ const SelectableButtonGroup = ({
|
|||||||
) : (
|
) : (
|
||||||
<Row gutter={gutterSize} style={{ lineHeight: '32px', ...style }}>
|
<Row gutter={gutterSize} style={{ lineHeight: '32px', ...style }}>
|
||||||
{items.map((item) => {
|
{items.map((item) => {
|
||||||
const isDisabled =
|
|
||||||
item.disabled ||
|
|
||||||
(typeof item.tagCount === 'number' && item.tagCount === 0);
|
|
||||||
const isActive = Array.isArray(activeValue)
|
const isActive = Array.isArray(activeValue)
|
||||||
? activeValue.includes(item.value)
|
? activeValue.includes(item.value)
|
||||||
: activeValue === item.value;
|
: activeValue === item.value;
|
||||||
@ -194,13 +192,11 @@ const SelectableButtonGroup = ({
|
|||||||
}}
|
}}
|
||||||
theme={isActive ? 'light' : 'outline'}
|
theme={isActive ? 'light' : 'outline'}
|
||||||
type={isActive ? 'primary' : 'tertiary'}
|
type={isActive ? 'primary' : 'tertiary'}
|
||||||
disabled={isDisabled}
|
|
||||||
className='sbg-button'
|
className='sbg-button'
|
||||||
icon={
|
icon={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isActive}
|
checked={isActive}
|
||||||
onChange={() => onChange(item.value)}
|
onChange={() => onChange(item.value)}
|
||||||
disabled={isDisabled}
|
|
||||||
style={{ pointerEvents: 'auto' }}
|
style={{ pointerEvents: 'auto' }}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@ -210,14 +206,9 @@ const SelectableButtonGroup = ({
|
|||||||
{item.icon && <span className='sbg-icon'>{item.icon}</span>}
|
{item.icon && <span className='sbg-icon'>{item.icon}</span>}
|
||||||
<ConditionalTooltipText text={item.label} />
|
<ConditionalTooltipText text={item.label} />
|
||||||
{item.tagCount !== undefined && shouldShowTags && (
|
{item.tagCount !== undefined && shouldShowTags && (
|
||||||
<Tag
|
<span className={`sbg-badge ${isActive ? 'sbg-badge-active' : ''}`}>
|
||||||
className='sbg-tag'
|
|
||||||
color='white'
|
|
||||||
shape='circle'
|
|
||||||
size='small'
|
|
||||||
>
|
|
||||||
{item.tagCount}
|
{item.tagCount}
|
||||||
</Tag>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
@ -231,22 +222,16 @@ 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'}
|
||||||
disabled={isDisabled}
|
|
||||||
className='sbg-button'
|
className='sbg-button'
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
>
|
>
|
||||||
<div className='sbg-content'>
|
<div className='sbg-content'>
|
||||||
{item.icon && <span className='sbg-icon'>{item.icon}</span>}
|
{item.icon && <span className='sbg-icon'>{item.icon}</span>}
|
||||||
<ConditionalTooltipText text={item.label} />
|
<ConditionalTooltipText text={item.label} />
|
||||||
{item.tagCount !== undefined && shouldShowTags && (
|
{item.tagCount !== undefined && shouldShowTags && item.tagCount !== '' && (
|
||||||
<Tag
|
<span className={`sbg-badge ${isActive ? 'sbg-badge-active' : ''}`}>
|
||||||
className='sbg-tag'
|
|
||||||
color='white'
|
|
||||||
shape='circle'
|
|
||||||
size='small'
|
|
||||||
>
|
|
||||||
{item.tagCount}
|
{item.tagCount}
|
||||||
</Tag>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
@ -258,7 +243,7 @@ const SelectableButtonGroup = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`mb-8 ${containerWidth <= 400 ? 'sbg-compact' : ''}`}
|
className={`mb-8 ${containerWidth <= 400 ? 'sbg-compact' : ''}${variant ? ` sbg-variant-${variant}` : ''}`}
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
>
|
>
|
||||||
{title && (
|
{title && (
|
||||||
|
|||||||
@ -76,7 +76,6 @@ const PricingEndpointTypes = ({
|
|||||||
value: 'all',
|
value: 'all',
|
||||||
label: t('全部端点'),
|
label: t('全部端点'),
|
||||||
tagCount: getEndpointTypeCount('all'),
|
tagCount: getEndpointTypeCount('all'),
|
||||||
disabled: models.length === 0,
|
|
||||||
},
|
},
|
||||||
...availableEndpointTypes.map((endpointType) => {
|
...availableEndpointTypes.map((endpointType) => {
|
||||||
const count = getEndpointTypeCount(endpointType);
|
const count = getEndpointTypeCount(endpointType);
|
||||||
@ -84,7 +83,6 @@ const PricingEndpointTypes = ({
|
|||||||
value: endpointType,
|
value: endpointType,
|
||||||
label: getEndpointTypeLabel(endpointType),
|
label: getEndpointTypeLabel(endpointType),
|
||||||
tagCount: count,
|
tagCount: count,
|
||||||
disabled: count === 0,
|
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
@ -96,6 +94,7 @@ const PricingEndpointTypes = ({
|
|||||||
activeValue={filterEndpointType}
|
activeValue={filterEndpointType}
|
||||||
onChange={setFilterEndpointType}
|
onChange={setFilterEndpointType}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
variant='green'
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -52,20 +52,19 @@ const PricingGroups = ({
|
|||||||
.length;
|
.length;
|
||||||
let ratioDisplay = '';
|
let ratioDisplay = '';
|
||||||
if (g === 'all') {
|
if (g === 'all') {
|
||||||
ratioDisplay = t('全部');
|
// ratioDisplay = t('全部');
|
||||||
} else {
|
} else {
|
||||||
const ratio = groupRatio[g];
|
const ratio = groupRatio[g];
|
||||||
if (ratio !== undefined && ratio !== null) {
|
if (ratio !== undefined && ratio !== null) {
|
||||||
ratioDisplay = `x${ratio}`;
|
ratioDisplay = `${ratio}x`;
|
||||||
} else {
|
} else {
|
||||||
ratioDisplay = 'x1';
|
ratioDisplay = '1x';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
value: g,
|
value: g,
|
||||||
label: g === 'all' ? t('全部分组') : g,
|
label: g === 'all' ? t('全部分组') : g,
|
||||||
tagCount: ratioDisplay,
|
tagCount: ratioDisplay,
|
||||||
disabled: modelCount === 0,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -76,6 +75,7 @@ const PricingGroups = ({
|
|||||||
activeValue={filterGroup}
|
activeValue={filterGroup}
|
||||||
onChange={setFilterGroup}
|
onChange={setFilterGroup}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
variant='teal'
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -52,6 +52,7 @@ const PricingQuotaTypes = ({
|
|||||||
activeValue={filterQuotaType}
|
activeValue={filterQuotaType}
|
||||||
onChange={setFilterQuotaType}
|
onChange={setFilterQuotaType}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
variant='amber'
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -78,7 +78,6 @@ const PricingTags = ({
|
|||||||
value: 'all',
|
value: 'all',
|
||||||
label: t('全部标签'),
|
label: t('全部标签'),
|
||||||
tagCount: getTagCount('all'),
|
tagCount: getTagCount('all'),
|
||||||
disabled: models.length === 0,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -88,7 +87,6 @@ const PricingTags = ({
|
|||||||
value: tag,
|
value: tag,
|
||||||
label: tag,
|
label: tag,
|
||||||
tagCount: count,
|
tagCount: count,
|
||||||
disabled: count === 0,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -102,6 +100,7 @@ const PricingTags = ({
|
|||||||
activeValue={filterTag}
|
activeValue={filterTag}
|
||||||
onChange={setFilterTag}
|
onChange={setFilterTag}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
variant='rose'
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -83,7 +83,6 @@ const PricingVendors = ({
|
|||||||
value: 'all',
|
value: 'all',
|
||||||
label: t('全部供应商'),
|
label: t('全部供应商'),
|
||||||
tagCount: getVendorCount('all'),
|
tagCount: getVendorCount('all'),
|
||||||
disabled: models.length === 0,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -96,7 +95,6 @@ const PricingVendors = ({
|
|||||||
label: vendor,
|
label: vendor,
|
||||||
icon: icon ? getLobeHubIcon(icon, 16) : null,
|
icon: icon ? getLobeHubIcon(icon, 16) : null,
|
||||||
tagCount: count,
|
tagCount: count,
|
||||||
disabled: count === 0,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -107,7 +105,6 @@ const PricingVendors = ({
|
|||||||
value: 'unknown',
|
value: 'unknown',
|
||||||
label: t('未知供应商'),
|
label: t('未知供应商'),
|
||||||
tagCount: count,
|
tagCount: count,
|
||||||
disabled: count === 0,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +118,7 @@ const PricingVendors = ({
|
|||||||
activeValue={filterVendor}
|
activeValue={filterVendor}
|
||||||
onChange={setFilterVendor}
|
onChange={setFilterVendor}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
variant='violet'
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -113,15 +113,6 @@ const PricingSidebar = ({
|
|||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PricingTags
|
|
||||||
filterTag={filterTag}
|
|
||||||
setFilterTag={setFilterTag}
|
|
||||||
models={tagModels}
|
|
||||||
allModels={categoryProps.models}
|
|
||||||
loading={loading}
|
|
||||||
t={t}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PricingGroups
|
<PricingGroups
|
||||||
filterGroup={filterGroup}
|
filterGroup={filterGroup}
|
||||||
setFilterGroup={handleGroupClick}
|
setFilterGroup={handleGroupClick}
|
||||||
@ -140,6 +131,15 @@ const PricingSidebar = ({
|
|||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<PricingTags
|
||||||
|
filterTag={filterTag}
|
||||||
|
setFilterTag={setFilterTag}
|
||||||
|
models={tagModels}
|
||||||
|
allModels={categoryProps.models}
|
||||||
|
loading={loading}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
|
|
||||||
<PricingEndpointTypes
|
<PricingEndpointTypes
|
||||||
filterEndpointType={filterEndpointType}
|
filterEndpointType={filterEndpointType}
|
||||||
setFilterEndpointType={setFilterEndpointType}
|
setFilterEndpointType={setFilterEndpointType}
|
||||||
|
|||||||
@ -96,15 +96,6 @@ const FilterModalContent = ({ sidebarProps, t }) => {
|
|||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PricingTags
|
|
||||||
filterTag={filterTag}
|
|
||||||
setFilterTag={setFilterTag}
|
|
||||||
models={tagModels}
|
|
||||||
allModels={categoryProps.models}
|
|
||||||
loading={loading}
|
|
||||||
t={t}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PricingGroups
|
<PricingGroups
|
||||||
filterGroup={filterGroup}
|
filterGroup={filterGroup}
|
||||||
setFilterGroup={setFilterGroup}
|
setFilterGroup={setFilterGroup}
|
||||||
@ -123,6 +114,16 @@ const FilterModalContent = ({ sidebarProps, t }) => {
|
|||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<PricingTags
|
||||||
|
filterTag={filterTag}
|
||||||
|
setFilterTag={setFilterTag}
|
||||||
|
models={tagModels}
|
||||||
|
allModels={categoryProps.models}
|
||||||
|
loading={loading}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
<PricingEndpointTypes
|
<PricingEndpointTypes
|
||||||
filterEndpointType={filterEndpointType}
|
filterEndpointType={filterEndpointType}
|
||||||
setFilterEndpointType={setFilterEndpointType}
|
setFilterEndpointType={setFilterEndpointType}
|
||||||
|
|||||||
96
web/src/index.css
vendored
96
web/src/index.css
vendored
@ -344,6 +344,102 @@ code {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Badge for count/multiplier in filter buttons */
|
||||||
|
.sbg-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
min-width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
padding: 0 6px;
|
||||||
|
border-radius: 9px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
line-height: 1;
|
||||||
|
background-color: var(--semi-color-fill-0);
|
||||||
|
color: var(--semi-color-text-2);
|
||||||
|
transition: background-color 0.15s ease, color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sbg-badge-active {
|
||||||
|
background-color: var(--semi-color-primary-light-active);
|
||||||
|
color: var(--semi-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- SelectableButtonGroup color variants ---- */
|
||||||
|
.sbg-variant-violet {
|
||||||
|
--semi-color-primary: #6d28d9;
|
||||||
|
--semi-color-primary-light-default: rgba(124, 58, 237, 0.08);
|
||||||
|
--semi-color-primary-light-hover: rgba(124, 58, 237, 0.15);
|
||||||
|
--semi-color-primary-light-active: rgba(124, 58, 237, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sbg-variant-teal {
|
||||||
|
--semi-color-primary: #0f766e;
|
||||||
|
--semi-color-primary-light-default: rgba(20, 184, 166, 0.08);
|
||||||
|
--semi-color-primary-light-hover: rgba(20, 184, 166, 0.15);
|
||||||
|
--semi-color-primary-light-active: rgba(20, 184, 166, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sbg-variant-amber {
|
||||||
|
--semi-color-primary: #b45309;
|
||||||
|
--semi-color-primary-light-default: rgba(245, 158, 11, 0.08);
|
||||||
|
--semi-color-primary-light-hover: rgba(245, 158, 11, 0.15);
|
||||||
|
--semi-color-primary-light-active: rgba(245, 158, 11, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sbg-variant-rose {
|
||||||
|
--semi-color-primary: #be123c;
|
||||||
|
--semi-color-primary-light-default: rgba(244, 63, 94, 0.08);
|
||||||
|
--semi-color-primary-light-hover: rgba(244, 63, 94, 0.15);
|
||||||
|
--semi-color-primary-light-active: rgba(244, 63, 94, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sbg-variant-green {
|
||||||
|
--semi-color-primary: #047857;
|
||||||
|
--semi-color-primary-light-default: rgba(16, 185, 129, 0.08);
|
||||||
|
--semi-color-primary-light-hover: rgba(16, 185, 129, 0.15);
|
||||||
|
--semi-color-primary-light-active: rgba(16, 185, 129, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark mode: lighter text, slightly stronger backgrounds */
|
||||||
|
html.dark .sbg-variant-violet {
|
||||||
|
--semi-color-primary: #a78bfa;
|
||||||
|
--semi-color-primary-light-default: rgba(139, 92, 246, 0.14);
|
||||||
|
--semi-color-primary-light-hover: rgba(139, 92, 246, 0.22);
|
||||||
|
--semi-color-primary-light-active: rgba(139, 92, 246, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .sbg-variant-teal {
|
||||||
|
--semi-color-primary: #2dd4bf;
|
||||||
|
--semi-color-primary-light-default: rgba(45, 212, 191, 0.14);
|
||||||
|
--semi-color-primary-light-hover: rgba(45, 212, 191, 0.22);
|
||||||
|
--semi-color-primary-light-active: rgba(45, 212, 191, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .sbg-variant-amber {
|
||||||
|
--semi-color-primary: #fbbf24;
|
||||||
|
--semi-color-primary-light-default: rgba(251, 191, 36, 0.14);
|
||||||
|
--semi-color-primary-light-hover: rgba(251, 191, 36, 0.22);
|
||||||
|
--semi-color-primary-light-active: rgba(251, 191, 36, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .sbg-variant-rose {
|
||||||
|
--semi-color-primary: #fb7185;
|
||||||
|
--semi-color-primary-light-default: rgba(251, 113, 133, 0.14);
|
||||||
|
--semi-color-primary-light-hover: rgba(251, 113, 133, 0.22);
|
||||||
|
--semi-color-primary-light-active: rgba(251, 113, 133, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark .sbg-variant-green {
|
||||||
|
--semi-color-primary: #34d399;
|
||||||
|
--semi-color-primary-light-default: rgba(52, 211, 153, 0.14);
|
||||||
|
--semi-color-primary-light-hover: rgba(52, 211, 153, 0.22);
|
||||||
|
--semi-color-primary-light-active: rgba(52, 211, 153, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
/* Tabs组件样式 */
|
/* Tabs组件样式 */
|
||||||
.semi-tabs-content {
|
.semi-tabs-content {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user