From 93c3f6d1d6b50a7ae06aca083aba5297d8d8b6e8 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 24 Nov 2024 17:40:15 -0800 Subject: [PATCH] Fixed: Truncating long text in the middle when it shouldn't be truncated Closes #7413 --- frontend/build/webpack.config.js | 3 +- .../src/Components/Form/Tag/TagInputTag.tsx | 4 +- frontend/src/Components/MiddleTruncate.tsx | 63 +++++++++++++++++++ ...eProfileRow.css => ReleaseProfileItem.css} | 0 ...w.css.d.ts => ReleaseProfileItem.css.d.ts} | 0 ...eProfileRow.tsx => ReleaseProfileItem.tsx} | 12 ++-- .../Profiles/Release/ReleaseProfiles.css | 2 +- .../Profiles/Release/ReleaseProfiles.tsx | 4 +- frontend/typings/MiddleTruncate.d.ts | 16 ----- package.json | 1 - yarn.lock | 34 +--------- 11 files changed, 78 insertions(+), 61 deletions(-) create mode 100644 frontend/src/Components/MiddleTruncate.tsx rename frontend/src/Settings/Profiles/Release/{ReleaseProfileRow.css => ReleaseProfileItem.css} (100%) rename frontend/src/Settings/Profiles/Release/{ReleaseProfileRow.css.d.ts => ReleaseProfileItem.css.d.ts} (100%) rename frontend/src/Settings/Profiles/Release/{ReleaseProfileRow.tsx => ReleaseProfileItem.tsx} (91%) delete mode 100644 frontend/typings/MiddleTruncate.d.ts diff --git a/frontend/build/webpack.config.js b/frontend/build/webpack.config.js index 53f8ef431..da97f7331 100644 --- a/frontend/build/webpack.config.js +++ b/frontend/build/webpack.config.js @@ -52,8 +52,7 @@ module.exports = (env) => { 'node_modules' ], alias: { - jquery: 'jquery/dist/jquery.min', - 'react-middle-truncate': 'react-middle-truncate/lib/react-middle-truncate' + jquery: 'jquery/dist/jquery.min' }, fallback: { buffer: false, diff --git a/frontend/src/Components/Form/Tag/TagInputTag.tsx b/frontend/src/Components/Form/Tag/TagInputTag.tsx index 484bf45e0..7b549767c 100644 --- a/frontend/src/Components/Form/Tag/TagInputTag.tsx +++ b/frontend/src/Components/Form/Tag/TagInputTag.tsx @@ -1,8 +1,8 @@ import React, { useCallback } from 'react'; -import MiddleTruncate from 'react-middle-truncate'; import Label, { LabelProps } from 'Components/Label'; import IconButton from 'Components/Link/IconButton'; import Link from 'Components/Link/Link'; +import MiddleTruncate from 'Components/MiddleTruncate'; import { icons } from 'Helpers/Props'; import { TagBase } from './TagInput'; import styles from './TagInputTag.css'; @@ -58,7 +58,7 @@ function TagInputTag({ tabIndex={-1} onPress={handleDelete} > - + {canEdit ? ( diff --git a/frontend/src/Components/MiddleTruncate.tsx b/frontend/src/Components/MiddleTruncate.tsx new file mode 100644 index 000000000..f635b0bc9 --- /dev/null +++ b/frontend/src/Components/MiddleTruncate.tsx @@ -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 ( +
+
+ {truncatedText} +
+
+ ); +} + +export default MiddleTruncate; diff --git a/frontend/src/Settings/Profiles/Release/ReleaseProfileRow.css b/frontend/src/Settings/Profiles/Release/ReleaseProfileItem.css similarity index 100% rename from frontend/src/Settings/Profiles/Release/ReleaseProfileRow.css rename to frontend/src/Settings/Profiles/Release/ReleaseProfileItem.css diff --git a/frontend/src/Settings/Profiles/Release/ReleaseProfileRow.css.d.ts b/frontend/src/Settings/Profiles/Release/ReleaseProfileItem.css.d.ts similarity index 100% rename from frontend/src/Settings/Profiles/Release/ReleaseProfileRow.css.d.ts rename to frontend/src/Settings/Profiles/Release/ReleaseProfileItem.css.d.ts diff --git a/frontend/src/Settings/Profiles/Release/ReleaseProfileRow.tsx b/frontend/src/Settings/Profiles/Release/ReleaseProfileItem.tsx similarity index 91% rename from frontend/src/Settings/Profiles/Release/ReleaseProfileRow.tsx rename to frontend/src/Settings/Profiles/Release/ReleaseProfileItem.tsx index d4cc963c2..58a7bd13f 100644 --- a/frontend/src/Settings/Profiles/Release/ReleaseProfileRow.tsx +++ b/frontend/src/Settings/Profiles/Release/ReleaseProfileItem.tsx @@ -1,9 +1,9 @@ import React, { useCallback } from 'react'; -import MiddleTruncate from 'react-middle-truncate'; import { useDispatch } from 'react-redux'; import { Tag } from 'App/State/TagsAppState'; import Card from 'Components/Card'; import Label from 'Components/Label'; +import MiddleTruncate from 'Components/MiddleTruncate'; import ConfirmModal from 'Components/Modal/ConfirmModal'; import TagList from 'Components/TagList'; import useModalOpenState from 'Helpers/Hooks/useModalOpenState'; @@ -13,14 +13,14 @@ import Indexer from 'typings/Indexer'; import ReleaseProfile from 'typings/Settings/ReleaseProfile'; import translate from 'Utilities/String/translate'; import EditReleaseProfileModal from './EditReleaseProfileModal'; -import styles from './ReleaseProfileRow.css'; +import styles from './ReleaseProfileItem.css'; interface ReleaseProfileProps extends ReleaseProfile { tagList: Tag[]; indexerList: Indexer[]; } -function ReleaseProfileRow(props: ReleaseProfileProps) { +function ReleaseProfileItem(props: ReleaseProfileProps) { const { id, name, @@ -70,7 +70,7 @@ function ReleaseProfileRow(props: ReleaseProfileProps) { return ( ); })} @@ -84,7 +84,7 @@ function ReleaseProfileRow(props: ReleaseProfileProps) { return ( ); })} @@ -128,4 +128,4 @@ function ReleaseProfileRow(props: ReleaseProfileProps) { ); } -export default ReleaseProfileRow; +export default ReleaseProfileItem; diff --git a/frontend/src/Settings/Profiles/Release/ReleaseProfiles.css b/frontend/src/Settings/Profiles/Release/ReleaseProfiles.css index 43f17b9dc..c8ec63e78 100644 --- a/frontend/src/Settings/Profiles/Release/ReleaseProfiles.css +++ b/frontend/src/Settings/Profiles/Release/ReleaseProfiles.css @@ -4,7 +4,7 @@ } .addReleaseProfile { - composes: releaseProfile from '~./ReleaseProfileRow.css'; + composes: releaseProfile from '~./ReleaseProfileItem.css'; background-color: var(--cardAlternateBackgroundColor); color: var(--gray); diff --git a/frontend/src/Settings/Profiles/Release/ReleaseProfiles.tsx b/frontend/src/Settings/Profiles/Release/ReleaseProfiles.tsx index 98300b1af..c0a34a46c 100644 --- a/frontend/src/Settings/Profiles/Release/ReleaseProfiles.tsx +++ b/frontend/src/Settings/Profiles/Release/ReleaseProfiles.tsx @@ -14,7 +14,7 @@ import createClientSideCollectionSelector from 'Store/Selectors/createClientSide import createTagsSelector from 'Store/Selectors/createTagsSelector'; import translate from 'Utilities/String/translate'; import EditReleaseProfileModal from './EditReleaseProfileModal'; -import ReleaseProfileRow from './ReleaseProfileRow'; +import ReleaseProfileItem from './ReleaseProfileItem'; import styles from './ReleaseProfiles.css'; function ReleaseProfiles() { @@ -59,7 +59,7 @@ function ReleaseProfiles() { {items.map((item) => { return ( - { - text: string; - ellipsis?: string; - start?: number | RegExp | string; - end?: number | RegExp | string; - smartCopy?: 'all' | 'partial'; - onResizeDebounceMs?: number; - } - - export default function MiddleTruncate( - props: MiddleTruncateProps - ): JSX.Element; -} diff --git a/package.json b/package.json index fe5079530..e619cfeca 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,6 @@ "react-google-recaptcha": "2.1.0", "react-lazyload": "3.2.0", "react-measure": "1.4.7", - "react-middle-truncate": "1.0.3", "react-popper": "1.3.7", "react-redux": "7.2.4", "react-router": "5.2.0", diff --git a/yarn.lock b/yarn.lock index d36aba5d1..4e1c21419 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2271,7 +2271,7 @@ chrome-trace-event@^1.0.2: resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== -classnames@2.5.1, classnames@^2.2.6: +classnames@2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== @@ -4120,11 +4120,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isnumeric@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/isnumeric/-/isnumeric-0.2.0.tgz#a2347ba360de19e33d0ffd590fddf7755cbf2e64" - integrity sha512-uSJoAwnN1eCKDFKi8hL3UCYJSkQv+NwhKzhevUPIn/QZ8ILO21f+wQnlZHU0eh1rsLO1gI4w/HQdeOSTKwlqMg== - isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" @@ -4464,7 +4459,7 @@ lodash.upperfirst@4.3.1: resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== -lodash@4.17.21, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: +lodash@4.17.21, lodash@^4.17.14, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4787,7 +4782,7 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== -normalize.css@8.0.1, normalize.css@^8.0.0: +normalize.css@8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== @@ -5491,16 +5486,6 @@ react-measure@1.4.7: prop-types "^15.5.4" resize-observer-polyfill "^1.4.1" -react-middle-truncate@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/react-middle-truncate/-/react-middle-truncate-1.0.3.tgz#42d198ad9738bc2d8f7b8e77e11e02107b856fe1" - integrity sha512-rBYJjSYgAvNayDk+yZz8QhQqbGLjsSZV2CuGJ4g18o6BUGlMgZ4fIOGKuIEIZj17zCXzSw7mCGAcZ4lw0y8Lgw== - dependencies: - classnames "^2.2.6" - lodash "^4.17.15" - normalize.css "^8.0.0" - units-css "^0.4.0" - react-popper@1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.7.tgz#f6a3471362ef1f0d10a4963673789de1baca2324" @@ -6771,14 +6756,6 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== -units-css@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/units-css/-/units-css-0.4.0.tgz#d6228653a51983d7c16ff28f8b9dc3b1ffed3a07" - integrity sha512-WijzYC+chwzg2D6HmNGUSzPAgFRJfuxVyG9oiY28Ei5E+g6fHoPkhXUr5GV+5hE/RTHZNd9SuX2KLioYHdttoA== - dependencies: - isnumeric "^0.2.0" - viewport-dimensions "^0.2.0" - universalify@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" @@ -6864,11 +6841,6 @@ value-equal@^1.0.1: resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== -viewport-dimensions@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/viewport-dimensions/-/viewport-dimensions-0.2.0.tgz#de740747db5387fd1725f5175e91bac76afdf36c" - integrity sha512-94JqlKxEP4m7WO+N3rm4tFRGXZmXXwSPQCoV+EPxDnn8YAGiLU3T+Ha1imLreAjXsHl0K+ELnIqv64i1XZHLFQ== - warning@^4.0.2, warning@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"