feat: Improve mobile text truncation and sidebar visibility
This commit is contained in:
@@ -80,7 +80,7 @@ const SiderBar = () => {
|
|||||||
itemKey: 'channel',
|
itemKey: 'channel',
|
||||||
to: '/channel',
|
to: '/channel',
|
||||||
icon: <IconLayers />,
|
icon: <IconLayers />,
|
||||||
className: isAdmin() ? 'semi-navigation-item-normal' : 'tableHiddle',
|
className: isAdmin() ? '' : 'tableHiddle',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t('聊天'),
|
text: t('聊天'),
|
||||||
@@ -101,7 +101,7 @@ const SiderBar = () => {
|
|||||||
icon: <IconCalendarClock />,
|
icon: <IconCalendarClock />,
|
||||||
className:
|
className:
|
||||||
localStorage.getItem('enable_data_export') === 'true'
|
localStorage.getItem('enable_data_export') === 'true'
|
||||||
? 'semi-navigation-item-normal'
|
? ''
|
||||||
: 'tableHiddle',
|
: 'tableHiddle',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -109,7 +109,7 @@ const SiderBar = () => {
|
|||||||
itemKey: 'redemption',
|
itemKey: 'redemption',
|
||||||
to: '/redemption',
|
to: '/redemption',
|
||||||
icon: <IconGift />,
|
icon: <IconGift />,
|
||||||
className: isAdmin() ? 'semi-navigation-item-normal' : 'tableHiddle',
|
className: isAdmin() ? '' : 'tableHiddle',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t('钱包'),
|
text: t('钱包'),
|
||||||
@@ -122,7 +122,7 @@ const SiderBar = () => {
|
|||||||
itemKey: 'user',
|
itemKey: 'user',
|
||||||
to: '/user',
|
to: '/user',
|
||||||
icon: <IconUser />,
|
icon: <IconUser />,
|
||||||
className: isAdmin() ? 'semi-navigation-item-normal' : 'tableHiddle',
|
className: isAdmin() ? '' : 'tableHiddle',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t('日志'),
|
text: t('日志'),
|
||||||
@@ -137,7 +137,7 @@ const SiderBar = () => {
|
|||||||
icon: <IconImage />,
|
icon: <IconImage />,
|
||||||
className:
|
className:
|
||||||
localStorage.getItem('enable_drawing') === 'true'
|
localStorage.getItem('enable_drawing') === 'true'
|
||||||
? 'semi-navigation-item-normal'
|
? ''
|
||||||
: 'tableHiddle',
|
: 'tableHiddle',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -147,7 +147,7 @@ const SiderBar = () => {
|
|||||||
icon: <IconChecklistStroked />,
|
icon: <IconChecklistStroked />,
|
||||||
className:
|
className:
|
||||||
localStorage.getItem('enable_task') === 'true'
|
localStorage.getItem('enable_task') === 'true'
|
||||||
? 'semi-navigation-item-normal'
|
? ''
|
||||||
: 'tableHiddle',
|
: 'tableHiddle',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
import { Modal, Tag, Typography } from '@douyinfe/semi-ui';
|
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) {
|
export function renderText(text, limit) {
|
||||||
if (text.length > limit) {
|
if (text.length > limit) {
|
||||||
@@ -67,6 +67,73 @@ export function renderRatio(ratio) {
|
|||||||
return <Tag color={color}>{ratio}x {i18next.t('倍率')}</Tag>;
|
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) => {
|
export const renderGroupOption = (item) => {
|
||||||
const {
|
const {
|
||||||
disabled,
|
disabled,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { SSE } from 'sse';
|
|||||||
import { IconSetting } from '@douyinfe/semi-icons';
|
import { IconSetting } from '@douyinfe/semi-icons';
|
||||||
import { StyleContext } from '../../context/Style/index.js';
|
import { StyleContext } from '../../context/Style/index.js';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { renderGroupOption } from '../../helpers/render.js';
|
import { renderGroupOption, truncateText } from '../../helpers/render.js';
|
||||||
|
|
||||||
const roleInfo = {
|
const roleInfo = {
|
||||||
user: {
|
user: {
|
||||||
@@ -99,9 +99,10 @@ const Playground = () => {
|
|||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
let localGroupOptions = Object.entries(data).map(([group, info]) => ({
|
let localGroupOptions = Object.entries(data).map(([group, info]) => ({
|
||||||
label: info.desc,
|
label: truncateText(info.desc, "50%"),
|
||||||
value: group,
|
value: group,
|
||||||
ratio: info.ratio
|
ratio: info.ratio,
|
||||||
|
fullLabel: info.desc // 保存完整文本用于tooltip
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (localGroupOptions.length === 0) {
|
if (localGroupOptions.length === 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user