mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
feat(TextEllipsis): add position prop (#12058)
* feat(TextEllipsis): add position prop * docs: update docs * chore: optimize code
This commit is contained in:
parent
0c47654da5
commit
3b9c72d09e
packages/vant/src/text-ellipsis
@ -81,17 +81,64 @@ export default {
|
||||
};
|
||||
```
|
||||
|
||||
### Custom Collapse Position
|
||||
|
||||
- Collapse the beginning part of the content:
|
||||
|
||||
```html
|
||||
<van-text-ellipsis
|
||||
rows="1"
|
||||
:content="text"
|
||||
expand-text="expand"
|
||||
collapse-text="collapse"
|
||||
position="start"
|
||||
/>
|
||||
```
|
||||
|
||||
```js
|
||||
export default {
|
||||
setup() {
|
||||
const text =
|
||||
"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.";
|
||||
return { text };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
- Collapse the middle part of the content:
|
||||
|
||||
```html
|
||||
<van-text-ellipsis
|
||||
rows="2"
|
||||
:content="text"
|
||||
expand-text="expand"
|
||||
collapse-text="collapse"
|
||||
position="middle"
|
||||
/>
|
||||
```
|
||||
|
||||
```js
|
||||
export default {
|
||||
setup() {
|
||||
const text =
|
||||
"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.";
|
||||
return { text };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| Attribute | Description | Type | Default |
|
||||
| ------------- | ------------------------ | ------------------ | ------- |
|
||||
| rows | Number of rows displayed | _number \| string_ | `1` |
|
||||
| content | The text displayed | _string_ | - |
|
||||
| expand-text | Expand operation text | _string_ | - |
|
||||
| collapse-text | Collapse operation text | _string_ | - |
|
||||
| dots `v4.2.0` | Text content of ellipsis | _string_ | `'...'` |
|
||||
| Attribute | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| rows | Number of rows displayed | _number \| string_ | `1` |
|
||||
| content | The text displayed | _string_ | - |
|
||||
| expand-text | Expand operation text | _string_ | - |
|
||||
| collapse-text | Collapse operation text | _string_ | - |
|
||||
| dots `v4.2.0` | Text content of ellipsis | _string_ | `'...'` |
|
||||
| position `v4.6.2` | Can be set to `start` `middle` | _string_ | `'end'` |
|
||||
|
||||
### Events
|
||||
|
||||
|
@ -76,17 +76,66 @@ export default {
|
||||
};
|
||||
```
|
||||
|
||||
### 自定义省略位置
|
||||
|
||||
通过设置 `position` 控制省略位置。
|
||||
|
||||
- 头部省略:
|
||||
|
||||
```html
|
||||
<van-text-ellipsis
|
||||
rows="1"
|
||||
:content="text"
|
||||
expand-text="展开"
|
||||
collapse-text="收起"
|
||||
position="start"
|
||||
/>
|
||||
```
|
||||
|
||||
```js
|
||||
export default {
|
||||
setup() {
|
||||
const text =
|
||||
'那一天我二十一岁,在我一生的黄金时代。我有好多奢望。我想爱,想吃,还想在一瞬间变成天上半明半暗的云。后来我才知道,生活就是个缓慢受锤的过程,人一天天老下去,奢望也一天天消失,最后变得像挨了锤的牛一样。可是我过二十一岁生日时没有预见到这一点。我觉得自己会永远生猛下去,什么也锤不了我。';
|
||||
return { text };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
- 中部省略:
|
||||
|
||||
```html
|
||||
<van-text-ellipsis
|
||||
rows="2"
|
||||
:content="text"
|
||||
expand-text="展开"
|
||||
collapse-text="收起"
|
||||
position="middle"
|
||||
/>
|
||||
```
|
||||
|
||||
```js
|
||||
export default {
|
||||
setup() {
|
||||
const text =
|
||||
'那一天我二十一岁,在我一生的黄金时代。我有好多奢望。我想爱,想吃,还想在一瞬间变成天上半明半暗的云。后来我才知道,生活就是个缓慢受锤的过程,人一天天老下去,奢望也一天天消失,最后变得像挨了锤的牛一样。可是我过二十一岁生日时没有预见到这一点。我觉得自己会永远生猛下去,什么也锤不了我。';
|
||||
return { text };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ------------- | ---------------- | ------------------ | ------- |
|
||||
| rows | 展示的行数 | _number \| string_ | `1` |
|
||||
| content | 需要展示的文本 | _string_ | - |
|
||||
| expand-text | 展开操作的文案 | _string_ | - |
|
||||
| collapse-text | 收起操作的文案 | _string_ | - |
|
||||
| dots `v4.2.0` | 省略号的文本内容 | _string_ | `'...'` |
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| rows | 展示的行数 | _number \| string_ | `1` |
|
||||
| content | 需要展示的文本 | _string_ | - |
|
||||
| expand-text | 展开操作的文案 | _string_ | - |
|
||||
| collapse-text | 收起操作的文案 | _string_ | - |
|
||||
| dots `v4.2.0` | 省略号的文本内容 | _string_ | `'...'` |
|
||||
| position `v4.6.2` | 省略位置,可选值为 `start` `middle` | _string_ | `'end'` |
|
||||
|
||||
### Events
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {
|
||||
ref,
|
||||
watch,
|
||||
computed,
|
||||
onMounted,
|
||||
defineComponent,
|
||||
type ExtractPropTypes,
|
||||
@ -20,6 +21,7 @@ export const textEllipsisProps = {
|
||||
content: makeStringProp(''),
|
||||
expandText: makeStringProp(''),
|
||||
collapseText: makeStringProp(''),
|
||||
position: makeStringProp('end'),
|
||||
};
|
||||
|
||||
export type TextEllipsisProps = ExtractPropTypes<typeof textEllipsisProps>;
|
||||
@ -37,6 +39,10 @@ export default defineComponent({
|
||||
const hasAction = ref(false);
|
||||
const root = ref<HTMLElement>();
|
||||
|
||||
const actionText = computed(() =>
|
||||
expanded.value ? props.expandText : props.collapseText
|
||||
);
|
||||
|
||||
const pxToNum = (value: string | null) => {
|
||||
if (!value) return 0;
|
||||
const match = value.match(/^\d*(\.\d*)?/);
|
||||
@ -70,33 +76,104 @@ export default defineComponent({
|
||||
container: HTMLDivElement,
|
||||
maxHeight: number
|
||||
) => {
|
||||
const { dots, content, expandText } = props;
|
||||
const { content, position, dots } = props;
|
||||
const end = content.length;
|
||||
|
||||
let left = 0;
|
||||
let right = content.length;
|
||||
let res = -1;
|
||||
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);
|
||||
}
|
||||
|
||||
while (left <= right) {
|
||||
const mid = Math.floor((left + right) / 2);
|
||||
container.innerText = content.slice(0, mid) + dots + expandText;
|
||||
if (container.offsetHeight <= maxHeight) {
|
||||
left = mid + 1;
|
||||
res = mid;
|
||||
} else {
|
||||
right = mid - 1;
|
||||
const middle = Math.round((left + right) >> 1);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (position === 'end') {
|
||||
return tail(middle, right);
|
||||
}
|
||||
|
||||
return tail(left, middle);
|
||||
};
|
||||
|
||||
container.innerText = tail(0, end);
|
||||
};
|
||||
|
||||
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[1]) +
|
||||
dots +
|
||||
dots +
|
||||
content.slice(rightPart[1], end)
|
||||
);
|
||||
}
|
||||
}
|
||||
return content.slice(0, res) + dots;
|
||||
|
||||
const leftMiddle = Math.floor((leftPart[0] + leftPart[1]) >> 1);
|
||||
const rightMiddle = Math.ceil((rightPart[0] + rightPart[1]) >> 1);
|
||||
|
||||
container.innerText =
|
||||
props.content.slice(0, leftMiddle) +
|
||||
props.dots +
|
||||
actionText.value +
|
||||
props.dots +
|
||||
props.content.slice(rightMiddle, end);
|
||||
|
||||
if (container.offsetHeight >= maxHeight) {
|
||||
return middleTail(
|
||||
[leftPart[0], leftMiddle],
|
||||
[rightMiddle, rightPart[1]]
|
||||
);
|
||||
}
|
||||
|
||||
return middleTail(
|
||||
[leftMiddle, leftPart[1]],
|
||||
[rightPart[0], rightMiddle]
|
||||
);
|
||||
};
|
||||
|
||||
const middle = (0 + end) >> 1;
|
||||
props.position === 'middle'
|
||||
? (container.innerText = middleTail([0, middle], [middle, end]))
|
||||
: calcEllipse();
|
||||
return container.innerText;
|
||||
};
|
||||
|
||||
// Calculate the interceptional text
|
||||
const container = cloneContainer();
|
||||
if (!container) return;
|
||||
|
||||
const { paddingBottom, paddingTop, lineHeight } = container.style;
|
||||
const maxHeight =
|
||||
const maxHeight = Math.ceil(
|
||||
(Number(props.rows) + 0.5) * pxToNum(lineHeight) +
|
||||
pxToNum(paddingTop) +
|
||||
pxToNum(paddingBottom);
|
||||
pxToNum(paddingTop) +
|
||||
pxToNum(paddingBottom)
|
||||
);
|
||||
|
||||
if (maxHeight < container.offsetHeight) {
|
||||
hasAction.value = true;
|
||||
text.value = calcEllipsisText(container, maxHeight);
|
||||
@ -121,7 +198,7 @@ export default defineComponent({
|
||||
|
||||
onMounted(calcEllipsised);
|
||||
|
||||
watch(() => [props.content, props.rows], calcEllipsised);
|
||||
watch(() => [props.content, props.rows, props.position], calcEllipsised);
|
||||
|
||||
useEventListener('resize', calcEllipsised);
|
||||
|
||||
|
@ -13,6 +13,9 @@ const t = useTranslate({
|
||||
collapseText: '收起',
|
||||
expandCollapse: '展开/收起',
|
||||
customRows: '自定义展示行数',
|
||||
collapsePosition: '自定义省略位置',
|
||||
collapseStart: '头部省略',
|
||||
collapseMiddle: '中部省略',
|
||||
},
|
||||
'en-US': {
|
||||
text1:
|
||||
@ -25,6 +28,9 @@ const t = useTranslate({
|
||||
collapseText: 'collapse',
|
||||
expandCollapse: 'Expand/Collapse',
|
||||
customRows: 'Customize Rows',
|
||||
collapsePosition: 'Custom Collapse Position',
|
||||
collapseStart: 'Head Area Collapse Position',
|
||||
collapseMiddle: 'Middle Area Collapse Position',
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@ -50,6 +56,28 @@ const t = useTranslate({
|
||||
:collapse-text="t('collapseText')"
|
||||
/>
|
||||
</demo-block>
|
||||
|
||||
<demo-block :title="t('collapsePosition')">
|
||||
<demo-block :title="t('collapseStart')">
|
||||
<van-text-ellipsis
|
||||
rows="1"
|
||||
:content="t('text3')"
|
||||
:expand-text="t('expandText')"
|
||||
:collapse-text="t('collapseText')"
|
||||
position="start"
|
||||
/>
|
||||
</demo-block>
|
||||
|
||||
<demo-block :title="t('collapseMiddle')">
|
||||
<van-text-ellipsis
|
||||
rows="2"
|
||||
:content="t('text3')"
|
||||
:expand-text="t('expandText')"
|
||||
:collapse-text="t('collapseText')"
|
||||
position="middle"
|
||||
/>
|
||||
</demo-block>
|
||||
</demo-block>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
|
@ -17,4 +17,17 @@ exports[`should render demo and match snapshot 1`] = `
|
||||
<div class="van-text-ellipsis">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<!--[-->
|
||||
<div>
|
||||
<!--[-->
|
||||
<div class="van-text-ellipsis">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<!--[-->
|
||||
<div class="van-text-ellipsis">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -3,14 +3,14 @@
|
||||
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...
|
||||
...
|
||||
<span class="van-text-ellipsis__action">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="van-text-ellipsis">
|
||||
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...
|
||||
...
|
||||
<span class="van-text-ellipsis__action">
|
||||
expand
|
||||
</span>
|
||||
@ -18,10 +18,28 @@ exports[`should render demo and match snapshot 1`] = `
|
||||
</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't foreseen any of it on my twenty-first birthday. I thought I would be vigorous forever, and that nothing could ever crush me...
|
||||
...
|
||||
<span class="van-text-ellipsis__action">
|
||||
expand
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<div class="van-text-ellipsis">
|
||||
...
|
||||
<span class="van-text-ellipsis__action">
|
||||
expand
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="van-text-ellipsis">
|
||||
......
|
||||
<span class="van-text-ellipsis__action">
|
||||
expand
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
Loading…
x
Reference in New Issue
Block a user