Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"stylus": "^0.54.8",
"stylus-loader": "^3.0.2",
"vue": "^2.6.11",
"vue-template-compiler": "^2.6.11"
"vue-template-compiler": "^2.6.11",
"text-clipper": "^2.1.0"
},
"peerDependencies": {
"vue": "^2.5.17"
Expand Down
142 changes: 142 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,80 @@
class="mt-2"
>{{ zh ? '截断状态:' + (clamped3 ? '已截断' : '未截断') : 'Clamped: ' + (clamped3 ? 'Yes' : 'No')}}</p>
</section>

<div class="divider text-center" data-content="raw html"/>
<section class="case">
<div class="form-horizontal">
<div class="form-group">
<label
class="form-label col-5 col-sm-12"
for="height4"
>{{ zh ? '容器高度' : 'Container height' }}</label>
<div class="col-7 col-sm-12 tooltip" :data-tooltip="`${height4}px`">
<input id="height4" v-model="height4" class="slider" type="range" min="10" max="400">
</div>
</div>
<div class="form-group">
<label
class="form-label col-5 col-sm-12"
for="width4"
>{{ zh ? '容器宽度' : 'Container width' }}</label>
<div class="col-7 col-sm-12 tooltip" :data-tooltip="`${width4}px`">
<input id="width4" v-model="width4" class="slider" type="range" min="240" max="600">
</div>
</div>
<div class="form-group">
<div class="col-3 col-sm-12">
<label class="form-checkbox">
<input v-model="rawHtml4" type="checkbox">
<i class="form-icon"/>
{{ 'HTML' }}
</label>
</div>
<div v-if="!zh" class="col-5 col-sm-12">
<label class="form-checkbox">
<input v-model="hyphens4" type="checkbox">
<i class="form-icon"/>
CSS Hyphens
</label>
</div>
<div v-if="!zh" class="col-3 col-sm-12">
<label class="form-checkbox">
<input v-model="rtl4" type="checkbox">
<i class="form-icon"/>
RTL
</label>
</div>
</div>
<div class="form-group">
<textarea v-model="textHtml" style="width: 100%" rows="3"/>
</div>
</div>
<v-clamp
:class="{
demo: true,
hyphens: hyphens4,
rtl: rtl4
}"
:max-height="height4 + 'px'"
autoresize
:style="{
width: `${width4}px`
}"
:raw-html="rawHtml4"
:clip="clipHTML"
>
{{ textHtml }}
<template #after="{ toggle, expanded, clamped }">
<button
v-if="expanded || clamped"
class="toggle btn btn-sm"
@click="toggle"
>{{ zh ? '切换' : 'Toggle' }}</button>
</template>
</v-clamp>
</section>

<h2 id="usage">
<a href="#usage">#</a>
{{ zh ? '使用方法' : 'Usage' }}
Expand Down Expand Up @@ -409,6 +483,56 @@ export default {
<code>false</code>
</p>
</li>
<li>
<p>
<code>raw-html: boolean</code>
</p>
<p>{{ zh ? '将文本呈现为 HTML。需要通过 `clip` prop 传入截断函数。' : 'Render text as HTML. Also need to pass in the clip function via `clip` prop.' }}</p>
<p>
{{ defaultText }}
<code>false</code>
</p>
</li>
<li>
<p>
<code>clip: (html: string, maxLength: number) => string</code>
</p>
<p>{{ zh ? '用于截断 HTML 的函数。' : 'A function for clipping HTML.' }}</p>
<details>
<summary>Example</summary>
<p>Example usage with <a taget="_blank" href="https://github.com/arendjr/text-clipper">text-clipper</a>

<pre v-pre class="code vue" data-lang="Vue"><code>&lt;template&gt;
&lt;v-clamp raw-html :clip="clipHTML"&gt;&#x7B;&#x7B; text &#x7D;&#x7D;&lt;/v-clamp&gt;
&lt;/template&gt;

&lt;script&gt;
import VClamp from 'vue-clamp'
import clip from 'text-clipper'

export default {
components: {
VClamp
},
data () {
return {
text: 'Some &lt;span&gt;html content&lt;/span&gt;.'
}
},
methods: {
clipHTML (html, maxLength) {
try {
return clip(html, maxLength, { html: true, breakWords: true })
} catch (error) {
return html.replace(/&lt;[^&gt;]*?&gt;/gi, '').slice(0, maxLength) + '...'
}
}
}
}
&lt;/script&gt;</code></pre>
</p>
</details>
</li>
</ul>
</section>
<div class="divider text-center" data-content="↓ Slots"/>
Expand Down Expand Up @@ -508,6 +632,7 @@ export default {

<script>
import VClamp from './components/Clamp'
import clip from 'text-clipper'
import qs from 'qs'
import hljs from 'highlight.js/lib/highlight.js'
import vue from './vue-lang.js'
Expand Down Expand Up @@ -545,10 +670,17 @@ export default {
hyphens3: true,
rtl3: false,
clamped3: false,
width4: 600,
height4: 200,
hyphens4: true,
rtl4: false,
rawHtml4: true,
text:
'Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is easy to pick up and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting libraries.',
textZh:
'Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。',
textHtml:
'<p>Vue Ecosystem <ul><li>Vue</li><li>Vuex</li><li>Vue Router</li><li>Vue SSR</li></ul></p> The easiest way to try out Vue.js is using the <a href="https://codesandbox.io/s/github/vuejs/vuejs.org/tree/master/src/v2/examples/vue-20-hello-world" target="_blank" rel="noopener">Hello World example</a>. Feel free to open it in another tab and follow along as we go through some basic examples. Or, you can <a href="https://github.com/vuejs/vuejs.org/blob/master/src/v2/examples/vue-20-hello-world/index.html" target="_blank" download="index.html" rel="noopener noreferrer">create an <code>index.html</code> file</a> and include Vue',
zh,
pascal: false
}
Expand Down Expand Up @@ -581,6 +713,16 @@ export default {
;[...this.$el.querySelectorAll('pre.code')].forEach(code => {
hljs.highlightBlock(code)
})
},
methods: {
clipHTML (html, maxLength) {
try {
return clip(html, maxLength, { html: true, breakWords: true })
} catch (error) {
console.warn(error)
return html.replace(/<[^>]*?>/gi, ' ').slice(0, maxLength) + '...'
}
}
}
}
</script>
Expand Down
57 changes: 45 additions & 12 deletions src/components/Clamp.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@ export default {
type: String,
default: '…'
},
expanded: Boolean
expanded: Boolean,
rawHtml: {
type: Boolean,
default: false
},
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather make rawHtml as a string, used like:

<v-clamp :raw-html="html" .../>

clip: {
type: Function,
default: undefined
}
},
data () {
return {
Expand All @@ -28,6 +36,14 @@ export default {
},
computed: {
clampedText () {
if (this.rawHtml) {
if (this.clip) {
return this.clip(this.text, this.offset)
} else {
console.warn('Need set `clip` function for raw-html')
}
}

return this.text.slice(0, this.offset) + this.ellipsis
},
isClamped () {
Expand All @@ -48,6 +64,11 @@ export default {
return null
}
return typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight
},
ariaLabel () {
return this.rawHtml
? this.text.replace(/<[^>]*?>/gi, ' ').trim()
: this.text.trim()
}
},
watch: {
Expand Down Expand Up @@ -78,7 +99,7 @@ export default {
(vm) => [vm.maxLines, vm.maxHeight, vm.ellipsis, vm.isClamped].join(),
this.update
)
this.$watch((vm) => [vm.tag, vm.text, vm.autoresize].join(), this.init)
this.$watch((vm) => [vm.tag, vm.text, vm.autoresize, vm.rawHtml].join(), this.init)
},
updated () {
this.text = this.getText()
Expand Down Expand Up @@ -171,7 +192,11 @@ export default {
this.applyChange()
},
applyChange () {
this.$refs.text.textContent = this.realText
if (this.rawHtml) {
this.$refs.text.innerHTML = this.realText
} else {
this.$refs.text.textContent = this.realText
}
},
stepToFit () {
this.fill()
Expand Down Expand Up @@ -211,18 +236,26 @@ export default {
}
},
render (h) {
const attributes = this.$isServer ? {}
: {
ref: 'text',
attrs: {
'aria-label': this.ariaLabel
}
}
const renderText = this.$isServer ? this.text : this.realText

if (this.rawHtml) {
attributes.domProps = {
innerHTML: renderText
}
}

const contents = [
h(
'span',
this.$isServer
? {}
: {
ref: 'text',
attrs: {
'aria-label': this.text.trim()
}
},
this.$isServer ? this.text : this.realText
attributes,
this.rawHtml ? undefined : renderText
)
]

Expand Down