feat: Improve mobile text truncation and sidebar visibility
This commit is contained in:
@@ -80,7 +80,7 @@ const SiderBar = () => {
|
||||
itemKey: 'channel',
|
||||
to: '/channel',
|
||||
icon: <IconLayers />,
|
||||
className: isAdmin() ? 'semi-navigation-item-normal' : 'tableHiddle',
|
||||
className: isAdmin() ? '' : 'tableHiddle',
|
||||
},
|
||||
{
|
||||
text: t('聊天'),
|
||||
@@ -101,7 +101,7 @@ const SiderBar = () => {
|
||||
icon: <IconCalendarClock />,
|
||||
className:
|
||||
localStorage.getItem('enable_data_export') === 'true'
|
||||
? 'semi-navigation-item-normal'
|
||||
? ''
|
||||
: 'tableHiddle',
|
||||
},
|
||||
{
|
||||
@@ -109,7 +109,7 @@ const SiderBar = () => {
|
||||
itemKey: 'redemption',
|
||||
to: '/redemption',
|
||||
icon: <IconGift />,
|
||||
className: isAdmin() ? 'semi-navigation-item-normal' : 'tableHiddle',
|
||||
className: isAdmin() ? '' : 'tableHiddle',
|
||||
},
|
||||
{
|
||||
text: t('钱包'),
|
||||
@@ -122,7 +122,7 @@ const SiderBar = () => {
|
||||
itemKey: 'user',
|
||||
to: '/user',
|
||||
icon: <IconUser />,
|
||||
className: isAdmin() ? 'semi-navigation-item-normal' : 'tableHiddle',
|
||||
className: isAdmin() ? '' : 'tableHiddle',
|
||||
},
|
||||
{
|
||||
text: t('日志'),
|
||||
@@ -137,7 +137,7 @@ const SiderBar = () => {
|
||||
icon: <IconImage />,
|
||||
className:
|
||||
localStorage.getItem('enable_drawing') === 'true'
|
||||
? 'semi-navigation-item-normal'
|
||||
? ''
|
||||
: 'tableHiddle',
|
||||
},
|
||||
{
|
||||
@@ -147,7 +147,7 @@ const SiderBar = () => {
|
||||
icon: <IconChecklistStroked />,
|
||||
className:
|
||||
localStorage.getItem('enable_task') === 'true'
|
||||
? 'semi-navigation-item-normal'
|
||||
? ''
|
||||
: 'tableHiddle',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import i18next from 'i18next';
|
||||
import { Modal, Tag, Typography } from '@douyinfe/semi-ui';
|
||||
import { copy, showSuccess } from './utils.js';
|
||||
import { copy, isMobile, showSuccess } from './utils.js';
|
||||
|
||||
export function renderText(text, limit) {
|
||||
if (text.length > limit) {
|
||||
@@ -67,6 +67,73 @@ export function renderRatio(ratio) {
|
||||
return <Tag color={color}>{ratio}x {i18next.t('倍率')}</Tag>;
|
||||
}
|
||||
|
||||
const measureTextWidth = (text, style = {
|
||||
fontSize: '14px',
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
||||
}, containerWidth) => {
|
||||
const span = document.createElement('span');
|
||||
|
||||
span.style.visibility = 'hidden';
|
||||
span.style.position = 'absolute';
|
||||
span.style.whiteSpace = 'nowrap';
|
||||
span.style.fontSize = style.fontSize;
|
||||
span.style.fontFamily = style.fontFamily;
|
||||
|
||||
span.textContent = text;
|
||||
|
||||
document.body.appendChild(span);
|
||||
const width = span.offsetWidth;
|
||||
|
||||
document.body.removeChild(span);
|
||||
|
||||
return width;
|
||||
};
|
||||
|
||||
export function truncateText(text, maxWidth = 200) {
|
||||
if (!isMobile()) {
|
||||
return text;
|
||||
}
|
||||
if (!text) return text;
|
||||
|
||||
try {
|
||||
// Handle percentage-based maxWidth
|
||||
let actualMaxWidth = maxWidth;
|
||||
if (typeof maxWidth === 'string' && maxWidth.endsWith('%')) {
|
||||
const percentage = parseFloat(maxWidth) / 100;
|
||||
// Use window width as fallback container width
|
||||
actualMaxWidth = window.innerWidth * percentage;
|
||||
}
|
||||
|
||||
const width = measureTextWidth(text);
|
||||
if (width <= actualMaxWidth) return text;
|
||||
|
||||
let left = 0;
|
||||
let right = text.length;
|
||||
let result = text;
|
||||
|
||||
while (left <= right) {
|
||||
const mid = Math.floor((left + right) / 2);
|
||||
const truncated = text.slice(0, mid) + '...';
|
||||
const currentWidth = measureTextWidth(truncated);
|
||||
|
||||
if (currentWidth <= actualMaxWidth) {
|
||||
result = truncated;
|
||||
left = mid + 1;
|
||||
} else {
|
||||
right = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.warn('Text measurement failed, falling back to character count', error);
|
||||
if (text.length > 20) {
|
||||
return text.slice(0, 17) + '...';
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
export const renderGroupOption = (item) => {
|
||||
const {
|
||||
disabled,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { SSE } from 'sse';
|
||||
import { IconSetting } from '@douyinfe/semi-icons';
|
||||
import { StyleContext } from '../../context/Style/index.js';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { renderGroupOption } from '../../helpers/render.js';
|
||||
import { renderGroupOption, truncateText } from '../../helpers/render.js';
|
||||
|
||||
const roleInfo = {
|
||||
user: {
|
||||
@@ -99,9 +99,10 @@ const Playground = () => {
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
let localGroupOptions = Object.entries(data).map(([group, info]) => ({
|
||||
label: info.desc,
|
||||
label: truncateText(info.desc, "50%"),
|
||||
value: group,
|
||||
ratio: info.ratio
|
||||
ratio: info.ratio,
|
||||
fullLabel: info.desc // 保存完整文本用于tooltip
|
||||
}));
|
||||
|
||||
if (localGroupOptions.length === 0) {
|
||||
|
||||
Reference in New Issue
Block a user