Skip to content

Commit

Permalink
fix(TextEllipsis): calculate the width of the action slot correctly (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
inottn authored Jul 20, 2024
1 parent 2a6b90b commit 8a2b339
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 74 deletions.
155 changes: 81 additions & 74 deletions packages/vant/src/text-ellipsis/TextEllipsis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
onActivated,
onMounted,
defineComponent,
nextTick,
type ExtractPropTypes,
} from 'vue';

Expand Down Expand Up @@ -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<HTMLElement>();
const actionRef = ref<HTMLElement>();
let needRecalculate = false;

const actionText = computed(() =>
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -210,13 +211,19 @@ export default defineComponent({
? slots.action({ expanded: expanded.value })
: actionText.value;
return (
<span class={bem('action')} onClick={onClickAction}>
<span ref={actionRef} class={bem('action')} onClick={onClickAction}>
{action}
</span>
);
};

onMounted(calcEllipsised);
onMounted(() => {
calcEllipsised();

if (slots.action) {
nextTick(calcEllipsised);
}
});

onActivated(() => {
if (needRecalculate) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,39 @@ exports[`should render demo and match snapshot 1`] = `
<div>
<!--[-->
<div class="van-text-ellipsis">
Take your time and be patient. Life itself will eventually answer all those questions it once raised for you.
</div>
</div>
<div>
<!--[-->
<div class="van-text-ellipsis">
The fleeting time of one&#39;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.
</div>
</div>
<div>
<!--[-->
<div class="van-text-ellipsis">
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&#39;t foreseen any of it on my twenty-first birthday. I thought I would be vigorous forever, and that nothing could ever crush me.
</div>
</div>
<div>
<!--[-->
<div>
<!--[-->
<div class="van-text-ellipsis">
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&#39;t foreseen any of it on my twenty-first birthday. I thought I would be vigorous forever, and that nothing could ever crush me.
</div>
</div>
<div>
<!--[-->
<div class="van-text-ellipsis">
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&#39;t foreseen any of it on my twenty-first birthday. I thought I would be vigorous forever, and that nothing could ever crush me.
</div>
</div>
<div>
<!--[-->
<div class="van-text-ellipsis">
Take your time and be patient. Life itself will eventually answer all those questions it once raised for you.
</div>
</div>
</div>
Expand Down

0 comments on commit 8a2b339

Please sign in to comment.