UNPKG

@carbon/ibm-products-web-components

Version:
258 lines (254 loc) 9.21 kB
/** * Copyright IBM Corp. 2024 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { __decorate } from 'tslib'; import { LitElement, html } from 'lit'; import { property, state, query } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import { styleMap } from 'lit/directives/style-map.js'; import { carbonElement } from '@carbon/web-components/es/globals/decorators/carbon-element.js'; import { prefix, carbonPrefix } from '../../globals/settings.js'; import '@carbon/web-components/es-custom/components/tooltip/index.js'; import '@carbon/web-components/es-custom/components/button/button.js'; import '@carbon/web-components/es-custom/components/link/index.js'; import styles from './truncated-text.scss.js'; const componentName = 'truncated-text'; const blockClass = `${prefix}--${componentName}`; const elementName = `${prefix}-${componentName}`; // c4p-truncated-text /** * TruncatedText. * * @element c4p-truncated-text */ let CDSTruncatedText = class CDSTruncatedText extends LitElement { constructor() { super(...arguments); /** * Specify how the tooltip should align with the content. */ this.align = 'top'; /** * Specify whether a auto align functionality should be applied */ this.autoalign = false; /** * The label on the collapse button. */ this.collapseLabel = ''; /** * The label on expand button. */ this.expandLabel = ''; /** * Unique identifier for the element. */ this.id = ''; /** * The maximum number of lines to display before truncation. */ this.lines = 0; /** * The method to display the full text when truncated. Options are "tooltip" or "expand". if not passed, the text would just be truncated with ellipsis. */ this.type = 'tooltip'; /** * The string value to be truncated. */ this.value = ''; this._isOverflowing = false; this._isExpanded = false; this._maxHeight = 'none'; this._lineHeight = 0; this._isLayered = false; } connectedCallback() { super.connectedCallback(); this._isLayered = !!this.closest(`${carbonPrefix}-layer`); this.type = this.type || 'tooltip'; } disconnectedCallback() { var _a; (_a = this._resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect(); super.disconnectedCallback(); } firstUpdated() { requestAnimationFrame(() => { const computedStyle = getComputedStyle(this._textElement); this._lineHeight = parseFloat(computedStyle.lineHeight); this._setupResizeObserver(); }); } updated(changed) { if (changed.has('lines') || changed.has('value')) { this._updateOverflowStatus(); this._updateMaxHeight(); } } _updateMaxHeight() { if (this.type !== 'expand') { return; } requestAnimationFrame(() => { if (!this._textElement) { return; } this._maxHeight = this.lines > 0 && !this._isExpanded ? `${this.lines * this._lineHeight}px` : `${this._textElement.scrollHeight}px`; }); } _setupResizeObserver() { if (!this._textElement) { return; } if (this._resizeObserver) { this._resizeObserver.disconnect(); } this._resizeObserver = new ResizeObserver(() => { this._updateOverflowStatus(); }); this._resizeObserver.observe(this); } _updateOverflowStatus() { if (!this._textElement || this.lines <= 0) { return; } this._updateMaxHeight(); const { scrollHeight, clientHeight } = this._textElement; const buffer = this._lineHeight / 2; // buffer of at least half of line height for a stable outcome const isOverflowing = scrollHeight > clientHeight + buffer; if (isOverflowing !== this._isOverflowing) { this._isOverflowing = isOverflowing; } } _handleKeydown(evt) { const { key } = evt; if (key === 'Enter' || key === ' ') { this._toggleExpansion(); } } _toggleExpansion() { var _a, _b, _c; this._isExpanded = !this._isExpanded; this._updateMaxHeight(); (_a = this._textElement) === null || _a === void 0 ? void 0 : _a.classList.add(`${blockClass}_transition`); const onTransitionEnd = () => { var _a, _b, _c; (_b = (_a = this._textElement) === null || _a === void 0 ? void 0 : _a.querySelector('button')) === null || _b === void 0 ? void 0 : _b.focus(); (_c = this._textElement) === null || _c === void 0 ? void 0 : _c.removeEventListener('transitionend', onTransitionEnd); }; (_b = this._textElement) === null || _b === void 0 ? void 0 : _b.addEventListener('transitionend', onTransitionEnd); /** * currently you cannot animate line-clamping * you can however animate max-height * this removes the clamping and then quickly adds it so you can see the ellipsis */ if (this._isExpanded === false) { (_c = this._textElement) === null || _c === void 0 ? void 0 : _c.classList.add(`${blockClass}_content--closing`); setTimeout(() => { var _a; (_a = this._textElement) === null || _a === void 0 ? void 0 : _a.classList.remove(`${blockClass}_content--closing`); }, 100); } } _renderToggleButton() { const className = classMap({ [`${blockClass}_button-collapse`]: this._isExpanded, [`${blockClass}_button-expand`]: !this._isExpanded, [`${blockClass}_button-layered`]: this._isLayered, [`${blockClass}_button-hide`]: !this._isOverflowing && !this._isExpanded, }); const label = this._isExpanded ? this.collapseLabel : this.expandLabel; return html ` <span aria-controls=${this.id} aria-expanded=${this._isExpanded} class=${className} @click=${this._toggleExpansion} @keydown=${this._handleKeydown} role="button" tabIndex="0" > ${label} </span> `; } render() { // Apply different styles based on truncation method const contentStyle = { ['--line-clamp']: this._isExpanded ? 'none' : this.lines, ['max-block-size']: this.type === 'expand' ? this._maxHeight : 'none', }; const valueBody = html ` <div id=${this.id} class="${blockClass}_content" style=${styleMap(contentStyle)} > ${this.value} </div> `; const tooltipVariant = html ` <cds-custom-tooltip align=${this.align} autoalign=${this.autoalign} enter-delay-ms="0" leave-delay-ms="0" > ${valueBody} <cds-custom-tooltip-content>${this.value}</cds-custom-tooltip-content> </cds-custom-tooltip> `; const expandVariant = html `${valueBody} ${this._renderToggleButton()}`; return this.type === 'tooltip' && this._isOverflowing ? tooltipVariant : expandVariant; } }; CDSTruncatedText.styles = styles; __decorate([ property({ reflect: true, type: String }) ], CDSTruncatedText.prototype, "align", void 0); __decorate([ property({ type: Boolean, reflect: true }) ], CDSTruncatedText.prototype, "autoalign", void 0); __decorate([ property({ attribute: 'collapse-label', type: String, reflect: true }) ], CDSTruncatedText.prototype, "collapseLabel", void 0); __decorate([ property({ attribute: 'expand-label', type: String, reflect: true }) ], CDSTruncatedText.prototype, "expandLabel", void 0); __decorate([ property({ type: String, reflect: true }) ], CDSTruncatedText.prototype, "id", void 0); __decorate([ property({ type: Number, reflect: true }) ], CDSTruncatedText.prototype, "lines", void 0); __decorate([ property({ type: String, reflect: true }) ], CDSTruncatedText.prototype, "type", void 0); __decorate([ property({ type: String, attribute: 'value', reflect: true }) ], CDSTruncatedText.prototype, "value", void 0); __decorate([ state() ], CDSTruncatedText.prototype, "_isOverflowing", void 0); __decorate([ state() ], CDSTruncatedText.prototype, "_isExpanded", void 0); __decorate([ state() ], CDSTruncatedText.prototype, "_maxHeight", void 0); __decorate([ query(`.${blockClass}_content`) ], CDSTruncatedText.prototype, "_textElement", void 0); CDSTruncatedText = __decorate([ carbonElement(elementName) ], CDSTruncatedText); var CDSTruncatedText$1 = CDSTruncatedText; export { CDSTruncatedText, blockClass, CDSTruncatedText$1 as default }; //# sourceMappingURL=truncated-text.js.map