UNPKG

wangbd-myui

Version:

MY前端基础框架核心组件库

375 lines (357 loc) 10.6 kB
/** * 基础文本组件 */ import {MyClipboard, MyIcon} from '$ui' import {Tooltip, Input} from 'element-ui' import isStyleSupport from '$ui/utils/styleChecker' import {addResizeListener, removeResizeListener} from 'element-ui/lib/utils/resize-event' import Editable from './Editable' import ellipsisHelper from './ellipsis' // 是否支持CSS多行文本省略号 const isLineClampSupport = isStyleSupport('webkitLineClamp') // 是否支持CSS单行文本省略号 const isTextOverflowSupport = isStyleSupport('textOverflow') const ELLIPSIS_STR = '...' /** * 添加包裹容器 * @param h * @param blocks * @param content * @return {*} */ function wrapperDecorations(h, blocks, content) { let currentContent = content; const {mark, code, underline, del, strong, pre} = blocks function wrap(needed, tag) { if (!needed) return; currentContent = h(tag, {}, [].concat(currentContent)); } wrap(pre, 'pre') wrap(strong, 'strong') wrap(underline, 'u') wrap(del, 'del') wrap(code, 'code') wrap(mark, 'mark') return currentContent } // 获取虚拟节点的文本 function getInnerText(children = []) { let texts = [] children.forEach(node => { if (node.children) { texts = texts.concat(getInnerText(node.children)) } if (node.text) { texts.push(node.text) } }) return texts.join('') } export default { components: { MyClipboard, MyIcon, Tooltip, Input, Editable }, /** * 属性参数 * @member props * @property {String} [tag=article] 生成标签名称 * @property {VNode[]} [children] 内容虚拟DOM * @property {Boolean} [copyable=false] 是否可拷贝 * @property {String} [copyText] 复制文本内容, 默认取虚拟DOM的innerText * @property {Boolean} [delete=false] 添加删除线样式 * @property {Boolean} [disabled=false] 禁用文本样式 * @property {Boolean} [editable=false] 是否可编辑 * @property {Boolean} [ellipsis=false] 设置自动溢出省略 * @property {Number} [rows=1] 超出几行显示省略, ellipsis true 有效 * @property {Boolean} [expandable=false] 显示展开按钮 ellipsis true 有效 * @property {Boolean|Object} [tooltip] 显示tooltip,ellipsis true 有效 * @property {Boolean} [mark=false] 添加标记样式 * @property {Boolean} [underline=false] 添加下划线样式 * @property {Boolean} [code=false] 添加引用样式 * @property {Boolean} [strong=false] 是否加粗 * @property {Boolean} [pre=false] 保留格式 * @property {Boolean} [small=false] 字体变小 * @property {String} [type] 文本类型 '', 'primary', 'secondary', 'warning', 'danger' */ props: { tag: { type: String, default: 'article' }, children: { type: Array }, // 是否可拷贝 copyable: Boolean, // 复制文本内容 copyText: String, // 添加删除线样式 delete: Boolean, // 禁用文本 disabled: Boolean, // 是否可编辑 editable: Boolean, // 设置自动溢出省略 ellipsis: Boolean, // ellipsis true 有效 rows: { type: Number, default: 1 }, // ellipsis true 有效 expandable: Boolean, // 显示tooltip,ellipsis true 有效, 设置tooltip后,expandable 将失效 tooltip: [Boolean, Object], // 添加标记样式 mark: Boolean, // 添加下划线样式 underline: Boolean, code: Boolean, strong: Boolean, pre: Boolean, small: Boolean, type: { type: String, default: '', validator(val) { return ['', 'primary', 'secondary', 'warning', 'danger'].includes(val) } } }, data() { this.copyTimerId = null return { copied: false, editing: false, ellipsisContent: null, isEllipsis: false, expanded: false } }, computed: { classes() { const cssEllipsis = this.canUseCSSEllipsis(); const cssTextOverflow = this.rows === 1 && cssEllipsis; const cssLineClamp = this.rows && this.rows > 1 && cssEllipsis; return { 'my-typography': true, [`is-${this.type}`]: !!this.type, 'is-disabled': this.disabled, 'is-editing': this.editing, 'is-ellipsis-single-line': (this.ellipsis && cssTextOverflow), 'is-ellipsis': (this.ellipsis && cssLineClamp), 'is-pointer': !!this.$listeners.click, 'is-small': this.small } }, styles() { const cssEllipsis = this.canUseCSSEllipsis(); const cssLineClamp = this.rows && this.rows > 1 && cssEllipsis; if (cssLineClamp) { return { '-webkit-line-clamp': this.rows } } return null }, copyIcon() { return this.copied ? 'el-icon-check' : 'el-icon-document-copy' } }, watch: { children() { this.resizeOnNextFrame() } }, methods: { // 检测是否可以用CSS实现省略号功能 canUseCSSEllipsis() { // 如果有操作相关功能,就不能用css实现 if (this.editable || this.copyable || this.expandable || this.tooltip) { return false } if (this.rows === 1) { return isTextOverflowSupport } return isLineClampSupport }, getText() { return getInnerText(this.children) }, setText(text) { if (this.children && this.children[0]) { this.children[0].text = text } }, getCopyText() { if (this.copyText) return this.copyText return this.getText() }, handleCopy(e) { this.copied = true clearTimeout(this.copyTimerId) this.copyTimerId = setTimeout(() => { this.copied = false }, 3000) /** * 复制文字时触发 * @event copy * @param {Object} 事件对象 */ this.$emit('copy', e) }, handleEdit() { this.editing = true /** * 开始编辑时触发 * @event editing */ this.$emit('editing') }, toggleExpand() { this.expanded = !this.expanded /** * 展开或收起时触发 * @event expand * @param {Boolean} expanded 是否展开 */ this.$emit('expand', this.expanded) }, handleEditorSave(text) { this.editing = false this.setText(text) /** * 编辑结束时触发 * @event edited * @param {String} text 文本内容 */ this.$emit('edited', text) this.resizeOnNextFrame() }, handleTextChange(text) { /** * 编辑文本改变时触发 * @event change * @param {String} text 文本内容 */ this.$emit('change', text) }, resizeOnNextFrame() { if (this.editing || !this.ellipsis) return this.$nextTick(() => { this.syncEllipsis() }) }, syncEllipsis() { if (!this.children || this.children.length === 0) return if (!this.rows || this.rows < 0) return if (this.canUseCSSEllipsis()) return const {content, ellipsis} = ellipsisHelper( this.$el, this.rows, this.children, this.renderOperations() ) this.ellipsisContent = content this.isEllipsis = ellipsis }, renderCopy() { if (!this.copyable) return return ( <my-clipboard class="my-typography__copy" tooltip text={this.getCopyText} onSuccess={this.handleCopy}> <my-icon name={this.copyIcon} class={{'my-typography__copy-icon': true, 'is-copied': this.copied}}></my-icon> </my-clipboard> ) }, renderEdit() { if (!this.editable) return return ( <Tooltip class="my-typography__edit" content="编辑" placement="top"> <span> <my-icon class="my-typography__edit-icon" name="el-icon-edit" onClick={this.handleEdit}></my-icon> </span> </Tooltip> ) }, renderExpand() { if (!this.ellipsis || !this.expandable || this.tooltip) return if (!this.isEllipsis) return return (<a class="my-typography__expand" onClick={this.toggleExpand}>{this.expanded ? '收起' : '展开'}</a>) }, renderEditInput() { return ( <Editable value={this.getText()} onChange={this.handleTextChange} onSave={this.handleEditorSave}></Editable> ) }, renderOperations() { return [this.renderExpand(), this.renderEdit(), this.renderCopy()].filter(vnode => vnode) }, renderTooltip(h, vnode) { if (!this.ellipsis || !this.tooltip) return vnode if (!this.isEllipsis) return vnode const props = { content: this.getText(), placement: 'top', ...this.tooltip } return ( <Tooltip {...{props}}>{vnode}</Tooltip> ) } }, render(h) { // 编辑模式,渲染编辑器 if (this.editing) { return h(this.tag, {class: this.classes}, [this.renderEditInput()]) } let children = [] // 需要js计算的显示省略号 if (this.isEllipsis && !this.expanded) { children = this.ellipsisContent.concat(ELLIPSIS_STR, this.renderOperations()) } else { // 无省略号 children = [].concat( wrapperDecorations(h, { strong: this.strong, mark: this.mark, underline: this.underline, del: this.delete, code: this.code, pre: this.pre }, this.children), this.renderOperations() ) } const vnode = h(this.tag, { class: this.classes, style: this.styles }, children) return this.renderTooltip(h, vnode) }, mounted() { if (this.ellipsis) { addResizeListener(this.tooltip ? this.$el.parentNode : this.$el, this.resizeOnNextFrame) this.resizeOnNextFrame() } }, beforeDestroy() { clearTimeout(this.copyTimerId) if (this.ellipsis) { removeResizeListener(this.tooltip ? this.$el.parentNode : this.$el, this.resizeOnNextFrame) } } }