mirror of
https://github.com/Radarr/Radarr
synced 2025-12-06 08:28:50 +01:00
Fixed: Truncate long text in tag values
(cherry picked from commit 93c3f6d1d6b50a7ae06aca083aba5297d8d8b6e8)
This commit is contained in:
parent
a9bbe06966
commit
efd2b80e10
6 changed files with 72 additions and 8 deletions
63
frontend/src/Components/MiddleTruncate.tsx
Normal file
63
frontend/src/Components/MiddleTruncate.tsx
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import useMeasure from 'Helpers/Hooks/useMeasure';
|
||||||
|
|
||||||
|
interface MiddleTruncateProps {
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTruncatedText(text: string, length: number) {
|
||||||
|
return `${text.slice(0, length)}...${text.slice(text.length - length)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MiddleTruncate({ text }: MiddleTruncateProps) {
|
||||||
|
const [containerRef, { width: containerWidth }] = useMeasure();
|
||||||
|
const [textRef, { width: textWidth }] = useMeasure();
|
||||||
|
const [truncatedText, setTruncatedText] = useState(text);
|
||||||
|
const truncatedTextRef = useRef(text);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTruncatedText(text);
|
||||||
|
}, [text]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!containerWidth || !textWidth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textWidth <= containerWidth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const characterLength = textWidth / text.length;
|
||||||
|
const charactersToRemove =
|
||||||
|
Math.ceil(text.length - containerWidth / characterLength) + 3;
|
||||||
|
let length = Math.ceil(text.length / 2 - charactersToRemove / 2);
|
||||||
|
|
||||||
|
let updatedText = getTruncatedText(text, length);
|
||||||
|
|
||||||
|
// Make sure if the text is still too long, we keep reducing the length
|
||||||
|
// each time we re-run this.
|
||||||
|
while (
|
||||||
|
updatedText.length >= truncatedTextRef.current.length &&
|
||||||
|
length > 10
|
||||||
|
) {
|
||||||
|
length--;
|
||||||
|
updatedText = getTruncatedText(text, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the value in the ref so we can compare it in the next render,
|
||||||
|
// without triggering this effect every time we change the text.
|
||||||
|
truncatedTextRef.current = updatedText;
|
||||||
|
setTruncatedText(updatedText);
|
||||||
|
}, [text, truncatedTextRef, containerWidth, textWidth]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={containerRef} style={{ whiteSpace: 'nowrap' }}>
|
||||||
|
<div ref={textRef} style={{ display: 'inline-block' }}>
|
||||||
|
{truncatedText}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MiddleTruncate;
|
||||||
|
|
@ -3,6 +3,7 @@ import { useDispatch } from 'react-redux';
|
||||||
import { Tag } from 'App/State/TagsAppState';
|
import { Tag } from 'App/State/TagsAppState';
|
||||||
import Card from 'Components/Card';
|
import Card from 'Components/Card';
|
||||||
import Label from 'Components/Label';
|
import Label from 'Components/Label';
|
||||||
|
import MiddleTruncate from 'Components/MiddleTruncate';
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||||
import TagList from 'Components/TagList';
|
import TagList from 'Components/TagList';
|
||||||
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
||||||
|
|
@ -12,14 +13,14 @@ import Indexer from 'typings/Indexer';
|
||||||
import ReleaseProfile from 'typings/Settings/ReleaseProfile';
|
import ReleaseProfile from 'typings/Settings/ReleaseProfile';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import EditReleaseProfileModal from './EditReleaseProfileModal';
|
import EditReleaseProfileModal from './EditReleaseProfileModal';
|
||||||
import styles from './ReleaseProfileRow.css';
|
import styles from './ReleaseProfileItem.css';
|
||||||
|
|
||||||
interface ReleaseProfileProps extends ReleaseProfile {
|
interface ReleaseProfileProps extends ReleaseProfile {
|
||||||
tagList: Tag[];
|
tagList: Tag[];
|
||||||
indexerList: Indexer[];
|
indexerList: Indexer[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReleaseProfileRow(props: ReleaseProfileProps) {
|
function ReleaseProfileItem(props: ReleaseProfileProps) {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
|
|
@ -69,7 +70,7 @@ function ReleaseProfileRow(props: ReleaseProfileProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Label key={item} className={styles.label} kind={kinds.SUCCESS}>
|
<Label key={item} className={styles.label} kind={kinds.SUCCESS}>
|
||||||
{item}
|
<MiddleTruncate text={item} />
|
||||||
</Label>
|
</Label>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
@ -83,7 +84,7 @@ function ReleaseProfileRow(props: ReleaseProfileProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Label key={item} className={styles.label} kind={kinds.DANGER}>
|
<Label key={item} className={styles.label} kind={kinds.DANGER}>
|
||||||
{item}
|
<MiddleTruncate text={item} />
|
||||||
</Label>
|
</Label>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
@ -127,4 +128,4 @@ function ReleaseProfileRow(props: ReleaseProfileProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ReleaseProfileRow;
|
export default ReleaseProfileItem;
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.addReleaseProfile {
|
.addReleaseProfile {
|
||||||
composes: releaseProfile from '~./ReleaseProfileRow.css';
|
composes: releaseProfile from '~./ReleaseProfileItem.css';
|
||||||
|
|
||||||
background-color: var(--cardAlternateBackgroundColor);
|
background-color: var(--cardAlternateBackgroundColor);
|
||||||
color: var(--gray);
|
color: var(--gray);
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ import Icon from 'Components/Icon';
|
||||||
import PageSectionContent from 'Components/Page/PageSectionContent';
|
import PageSectionContent from 'Components/Page/PageSectionContent';
|
||||||
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import ReleaseProfileRow from 'Settings/Profiles/Release/ReleaseProfileRow';
|
|
||||||
import { fetchIndexers } from 'Store/Actions/Settings/indexers';
|
import { fetchIndexers } from 'Store/Actions/Settings/indexers';
|
||||||
import { fetchReleaseProfiles } from 'Store/Actions/Settings/releaseProfiles';
|
import { fetchReleaseProfiles } from 'Store/Actions/Settings/releaseProfiles';
|
||||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import EditReleaseProfileModal from './EditReleaseProfileModal';
|
import EditReleaseProfileModal from './EditReleaseProfileModal';
|
||||||
|
import ReleaseProfileItem from './ReleaseProfileItem';
|
||||||
import styles from './ReleaseProfiles.css';
|
import styles from './ReleaseProfiles.css';
|
||||||
|
|
||||||
function ReleaseProfiles() {
|
function ReleaseProfiles() {
|
||||||
|
|
@ -59,7 +59,7 @@ function ReleaseProfiles() {
|
||||||
|
|
||||||
{items.map((item) => {
|
{items.map((item) => {
|
||||||
return (
|
return (
|
||||||
<ReleaseProfileRow
|
<ReleaseProfileItem
|
||||||
key={item.id}
|
key={item.id}
|
||||||
tagList={tagList}
|
tagList={tagList}
|
||||||
indexerList={indexerList}
|
indexerList={indexerList}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue