From 8a2b339d61294477afb12c18055603bf462a6bbf Mon Sep 17 00:00:00 2001 From: inottn Date: Sat, 20 Jul 2024 21:39:26 +0800 Subject: [PATCH] fix(TextEllipsis): calculate the width of the action slot correctly (#13005) --- .../vant/src/text-ellipsis/TextEllipsis.tsx | 155 +++++++++--------- .../test/__snapshots__/demo-ssr.spec.ts.snap | 6 + 2 files changed, 87 insertions(+), 74 deletions(-) diff --git a/packages/vant/src/text-ellipsis/TextEllipsis.tsx b/packages/vant/src/text-ellipsis/TextEllipsis.tsx index 4d4ea69edab..7f5c7789246 100644 --- a/packages/vant/src/text-ellipsis/TextEllipsis.tsx +++ b/packages/vant/src/text-ellipsis/TextEllipsis.tsx @@ -5,6 +5,7 @@ import { onActivated, onMounted, defineComponent, + nextTick, type ExtractPropTypes, } from 'vue'; @@ -39,10 +40,11 @@ export default defineComponent({ emits: ['clickAction'], setup(props, { emit, slots }) { - const text = ref(''); + const text = ref(props.content); const expanded = ref(false); const hasAction = ref(false); const root = ref(); + const actionRef = ref(); let needRecalculate = false; const actionText = computed(() => @@ -79,97 +81,96 @@ export default defineComponent({ return container; }; - const calcEllipsised = () => { - const calcEllipsisText = ( - container: HTMLDivElement, - maxHeight: number, - ) => { - const { content, position, dots } = props; - const end = content.length; - - const calcEllipse = () => { - // calculate the former or later content - const tail = (left: number, right: number): string => { - if (right - left <= 1) { - if (position === 'end') { - return content.slice(0, left) + dots; - } - return dots + content.slice(right, end); + const calcEllipsisText = (container: HTMLDivElement, maxHeight: number) => { + const { content, position, dots } = props; + const end = content.length; + const middle = (0 + end) >> 1; + const actionHTML = slots.action + ? actionRef.value?.outerHTML ?? '' + : props.expandText; + + const calcEllipse = () => { + // calculate the former or later content + const tail = (left: number, right: number): string => { + if (right - left <= 1) { + if (position === 'end') { + return content.slice(0, left) + dots; } + return dots + content.slice(right, end); + } - const middle = Math.round((left + right) / 2); + const middle = Math.round((left + right) / 2); - // Set the interception location - if (position === 'end') { - container.innerText = - content.slice(0, middle) + dots + actionText.value; - } else { - container.innerText = - dots + content.slice(middle, end) + actionText.value; - } + // Set the interception location + if (position === 'end') { + container.innerText = content.slice(0, middle) + dots; + } else { + container.innerText = dots + content.slice(middle, end); + } - // The height after interception still does not match the rquired height - if (container.offsetHeight > maxHeight) { - if (position === 'end') { - return tail(left, middle); - } - return tail(middle, right); - } + container.innerHTML += actionHTML; + // The height after interception still does not match the rquired height + if (container.offsetHeight > maxHeight) { if (position === 'end') { - return tail(middle, right); + return tail(left, middle); } + return tail(middle, right); + } - return tail(left, middle); - }; + if (position === 'end') { + return tail(middle, right); + } - container.innerText = tail(0, end); + return tail(left, middle); }; - const middleTail = ( - leftPart: [number, number], - rightPart: [number, number], - ): string => { - if ( - leftPart[1] - leftPart[0] <= 1 && - rightPart[1] - rightPart[0] <= 1 - ) { - return ( - content.slice(0, leftPart[0]) + - dots + - content.slice(rightPart[1], end) - ); - } + return tail(0, end); + }; - const leftMiddle = Math.floor((leftPart[0] + leftPart[1]) / 2); - const rightMiddle = Math.ceil((rightPart[0] + rightPart[1]) / 2); + const middleTail = ( + leftPart: [number, number], + rightPart: [number, number], + ): string => { + if ( + leftPart[1] - leftPart[0] <= 1 && + rightPart[1] - rightPart[0] <= 1 + ) { + return ( + content.slice(0, leftPart[0]) + + dots + + content.slice(rightPart[1], end) + ); + } - container.innerText = - props.content.slice(0, leftMiddle) + - props.dots + - props.content.slice(rightMiddle, end) + - props.expandText; + const leftMiddle = Math.floor((leftPart[0] + leftPart[1]) / 2); + const rightMiddle = Math.ceil((rightPart[0] + rightPart[1]) / 2); - if (container.offsetHeight >= maxHeight) { - return middleTail( - [leftPart[0], leftMiddle], - [rightMiddle, rightPart[1]], - ); - } + container.innerText = + props.content.slice(0, leftMiddle) + + props.dots + + props.content.slice(rightMiddle, end); + container.innerHTML += actionHTML; + if (container.offsetHeight >= maxHeight) { return middleTail( - [leftMiddle, leftPart[1]], - [rightPart[0], rightMiddle], + [leftPart[0], leftMiddle], + [rightMiddle, rightPart[1]], ); - }; + } - const middle = (0 + end) >> 1; - props.position === 'middle' - ? (container.innerText = middleTail([0, middle], [middle, end])) - : calcEllipse(); - return container.innerText; + return middleTail( + [leftMiddle, leftPart[1]], + [rightPart[0], rightMiddle], + ); }; + return props.position === 'middle' + ? middleTail([0, middle], [middle, end]) + : calcEllipse(); + }; + + const calcEllipsised = () => { // Calculate the interceptional text const container = cloneContainer(); @@ -210,13 +211,19 @@ export default defineComponent({ ? slots.action({ expanded: expanded.value }) : actionText.value; return ( - + {action} ); }; - onMounted(calcEllipsised); + onMounted(() => { + calcEllipsised(); + + if (slots.action) { + nextTick(calcEllipsised); + } + }); onActivated(() => { if (needRecalculate) { diff --git a/packages/vant/src/text-ellipsis/test/__snapshots__/demo-ssr.spec.ts.snap b/packages/vant/src/text-ellipsis/test/__snapshots__/demo-ssr.spec.ts.snap index bfd7f5f834a..fa970278336 100644 --- a/packages/vant/src/text-ellipsis/test/__snapshots__/demo-ssr.spec.ts.snap +++ b/packages/vant/src/text-ellipsis/test/__snapshots__/demo-ssr.spec.ts.snap @@ -5,16 +5,19 @@ exports[`should render demo and match snapshot 1`] = `
+ Take your time and be patient. Life itself will eventually answer all those questions it once raised for you.
+ The fleeting time of one's life is everything that belongs to a person. Only this thing truly belongs to you. Everything else is just a momentary pleasure or misfortune, which will soon be gone with the passing of time.
+ That day, I turned twenty-one. In the golden age of my life, I was full of dreams. I wanted to love, to eat, and to instantly transform into one of these clouds, part alight, part darkened. It was only later that I understood life is but a slow, drawn-out process of getting your balls crushed. Day by day, you get older. Day by day, your dreams fade. In the end you are no different from a crushed ox. But I hadn't foreseen any of it on my twenty-first birthday. I thought I would be vigorous forever, and that nothing could ever crush me.
@@ -22,16 +25,19 @@ exports[`should render demo and match snapshot 1`] = `
+ That day, I turned twenty-one. In the golden age of my life, I was full of dreams. I wanted to love, to eat, and to instantly transform into one of these clouds, part alight, part darkened. It was only later that I understood life is but a slow, drawn-out process of getting your balls crushed. Day by day, you get older. Day by day, your dreams fade. In the end you are no different from a crushed ox. But I hadn't foreseen any of it on my twenty-first birthday. I thought I would be vigorous forever, and that nothing could ever crush me.
+ That day, I turned twenty-one. In the golden age of my life, I was full of dreams. I wanted to love, to eat, and to instantly transform into one of these clouds, part alight, part darkened. It was only later that I understood life is but a slow, drawn-out process of getting your balls crushed. Day by day, you get older. Day by day, your dreams fade. In the end you are no different from a crushed ox. But I hadn't foreseen any of it on my twenty-first birthday. I thought I would be vigorous forever, and that nothing could ever crush me.
+ Take your time and be patient. Life itself will eventually answer all those questions it once raised for you.