@carbon/ibm-products-web-components
Version:
Carbon for IBM Products Web Components
258 lines (254 loc) • 9.16 kB
JavaScript
/**
* 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/components/tooltip/index.js';
import '@carbon/web-components/es/components/button/button.js';
import '@carbon/web-components/es/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}
=${this._toggleExpansion}
=${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-tooltip
align=${this.align}
autoalign=${this.autoalign}
enter-delay-ms="0"
leave-delay-ms="0"
>
${valueBody}
<cds-tooltip-content>${this.value}</cds-tooltip-content>
</cds-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