@gitlab/ui
Version:
GitLab UI Components
156 lines (145 loc) • 6.57 kB
JavaScript
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 };