UNPKG

@gitlab/ui

Version:
156 lines (145 loc) 6.57 kB
import { GlTooltipDirective } from '../../../directives/tooltip/tooltip'; import { GlResizeObserverDirective } from '../../../directives/resize_observer/resize_observer'; import { POSITION, ZERO_WIDTH_SPACE } from './constants'; import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js'; // var script = { name: 'GlTruncate', POSITION, directives: { GlTooltip: GlTooltipDirective, GlResizeObserver: GlResizeObserverDirective }, props: { /** * Text to be ellipsized */ text: { type: String, required: true }, /** * Ellipsis position */ position: { type: String, required: false, default: POSITION.END, validator: value => Object.values(POSITION).includes(value) }, /** * Display the full text in a tooltip only if it is being truncated */ withTooltip: { type: Boolean, required: false, default: false } }, data() { return { isTruncated: false }; }, computed: { middleIndex() { return Math.floor(this.text.length / 2); }, preventWhitespaceCollapse() { // We don't use `\s` here since it includes non-breaking spaces and other // non-collapsible whitespace characters. // See https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace // and https://infra.spec.whatwg.org/#ascii-whitespace. const collapsibleWhitespaceChar = /^[ \n\t\r\f]$/; const { text, middleIndex } = this; const lastCharOfFirstIsCollapsibleWhitespace = collapsibleWhitespaceChar.test(text.charAt(middleIndex - 1)); const firstCharOfLastIsCollapsibleWhitespace = collapsibleWhitespaceChar.test(text.charAt(middleIndex)); return lastCharOfFirstIsCollapsibleWhitespace && !firstCharOfLastIsCollapsibleWhitespace; }, first() { const first = this.text.slice(0, this.middleIndex); if (this.preventWhitespaceCollapse) { // Because this component's root element has an internal flex layout, // whitespace at the end of the first child span and at the beginning // of the second child span would be ignored (i.e., totally collapsed). // // This means that strings with a space character in the middle would // render as if there were no space, which would be incorrect (e.g., // "Gap here" would render as "Gaphere"). // // So, in that case, we insert a zero-width space at the end of the // first child span to prevent that whitespace from being totally // collapsed. In other words: // 'first-part-with-space ' → 'first-part-with-space ​' // // If there's a whitespace character at the begging of the second child // span, we do *not* do this, since the left-to-right mark (‎) just // before `{{ last }}` in the template prevents the collapse of any // whitespace at the start of `last`. If we ignored this edge case, // we'd render a double space, which wouldn't correspond to how the // string would normally render. // // See // https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace#whitespace_in_block_formatting_contexts // for more information on how browsers treat whitespace. return `${first}${ZERO_WIDTH_SPACE}`; } return first; }, last() { return this.text.slice(this.middleIndex); }, isTooltipDisabled() { return !this.withTooltip || !this.isTruncated; }, title() { return this.withTooltip ? this.text : undefined; } }, watch: { withTooltip(withTooltip) { if (withTooltip) { this.checkTruncationState(); } } }, methods: { checkTruncationState() { if (this.withTooltip) { this.isTruncated = this.$refs.text.scrollWidth > this.$refs.text.offsetWidth; } } } }; /* script */ const __vue_script__ = script; /* template */ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.position === _vm.$options.POSITION.START)?_c('span',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip",value:({ disabled: _vm.isTooltipDisabled }),expression:"{ disabled: isTooltipDisabled }"},{name:"gl-resize-observer",rawName:"v-gl-resize-observer:[withTooltip]",value:(_vm.checkTruncationState),expression:"checkTruncationState",arg:_vm.withTooltip}],staticClass:"gl-truncate-component",attrs:{"title":_vm.title}},[_c('span',{ref:"text",staticClass:"gl-truncate-start !gl-text-ellipsis"},[_vm._v("‎"+_vm._s(_vm.text)+"‎")])]):(_vm.position === _vm.$options.POSITION.MIDDLE)?_c('span',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip",value:({ disabled: _vm.isTooltipDisabled }),expression:"{ disabled: isTooltipDisabled }"},{name:"gl-resize-observer",rawName:"v-gl-resize-observer:[withTooltip]",value:(_vm.checkTruncationState),expression:"checkTruncationState",arg:_vm.withTooltip}],staticClass:"gl-truncate-component",attrs:{"title":_vm.title}},[_c('span',{ref:"text",staticClass:"gl-truncate-end"},[_vm._v(_vm._s(_vm.first))]),_c('span',{staticClass:"gl-truncate-start"},[_vm._v("‎"+_vm._s(_vm.last)+"‎")])]):_c('span',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip",value:({ disabled: _vm.isTooltipDisabled }),expression:"{ disabled: isTooltipDisabled }"},{name:"gl-resize-observer",rawName:"v-gl-resize-observer:[withTooltip]",value:(_vm.checkTruncationState),expression:"checkTruncationState",arg:_vm.withTooltip}],staticClass:"gl-truncate-component",attrs:{"data-testid":"truncate-end-container","title":_vm.title}},[_c('span',{ref:"text",staticClass:"gl-truncate-end"},[_vm._v(_vm._s(_vm.text))])])}; var __vue_staticRenderFns__ = []; /* style */ const __vue_inject_styles__ = undefined; /* scoped */ const __vue_scope_id__ = undefined; /* module identifier */ const __vue_module_identifier__ = undefined; /* functional template */ const __vue_is_functional_template__ = false; /* style inject */ /* style inject SSR */ /* style inject shadow dom */ const __vue_component__ = /*#__PURE__*/__vue_normalize__( { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, false, undefined, undefined, undefined ); export { __vue_component__ as default };