@carbon/ibm-products-web-components
Version:
Carbon for IBM Products Web Components
221 lines (219 loc) • 7.41 kB
JavaScript
/**
* Copyright IBM Corp. 2020, 2026
*
* 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 "../../globals/settings.js";
import { __decorate } from "../../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.js";
import truncated_text_default$1 from "./truncated-text.scss.js";
import { classMap } from "lit/directives/class-map.js";
import { LitElement, html } from "lit";
import { property, query, state } from "lit/decorators.js";
import { carbonElement } from "@carbon/web-components/es/globals/decorators/carbon-element.js";
import { styleMap } from "lit/directives/style-map.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';
//#region src/components/truncated-text/truncated-text.ts
/**
* @license
*
* Copyright IBM Corp. 2025, 2025
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
const componentName = "truncated-text";
const blockClass = `c4p--${componentName}`;
const elementName = `c4p-${componentName}`;
let CDSTruncatedText = class CDSTruncatedText extends LitElement {
constructor(..._args) {
super(..._args);
this.align = "top";
this.autoalign = false;
this.collapseLabel = "";
this.expandLabel = "";
this.id = "";
this.lines = 0;
this.type = "tooltip";
this.value = "";
this._isOverflowing = false;
this._isExpanded = false;
this._maxHeight = "none";
this._lineHeight = 0;
this._isLayered = false;
}
static {
this.styles = truncated_text_default$1;
}
connectedCallback() {
super.connectedCallback();
this._isLayered = !!this.closest(`cds-custom-layer`);
this.type = this.type || "tooltip";
}
disconnectedCallback() {
this._resizeObserver?.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 isOverflowing = scrollHeight > clientHeight + this._lineHeight / 2;
if (isOverflowing !== this._isOverflowing) this._isOverflowing = isOverflowing;
}
_handleKeydown(evt) {
const { key } = evt;
if (key === "Enter" || key === " ") this._toggleExpansion();
}
_toggleExpansion() {
this._isExpanded = !this._isExpanded;
this._updateMaxHeight();
this._textElement?.classList.add(`${blockClass}_transition`);
const onTransitionEnd = () => {
this._textElement?.querySelector("button")?.focus();
this._textElement?.removeEventListener("transitionend", onTransitionEnd);
};
this._textElement?.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) {
this._textElement?.classList.add(`${blockClass}_content--closing`);
setTimeout(() => {
this._textElement?.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() {
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 = this._isOverflowing ? html`
<cds-custom-tooltip
align=${this.align}
autoalign=${this.autoalign}
enter-delay-ms="0"
leave-delay-ms="0"
>
<button
type="button"
class="${blockClass}_tooltip-trigger"
aria-label=${this.value}
>
${valueBody}
</button>
<cds-custom-tooltip-content>${this.value}</cds-custom-tooltip-content>
</cds-custom-tooltip>
` : valueBody;
const expandVariant = html`${valueBody} ${this._renderToggleButton()}`;
return this.type === "tooltip" && this._isOverflowing ? tooltipVariant : expandVariant;
}
};
__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 truncated_text_default = CDSTruncatedText;
//#endregion
export { CDSTruncatedText, blockClass, truncated_text_default as default };
//# sourceMappingURL=truncated-text.js.map