Skip to content

Instantly share code, notes, and snippets.

@tdwesten
Created April 18, 2023 19:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tdwesten/a44b1121d35fe2af9e2ec7cb384e40ec to your computer and use it in GitHub Desktop.
Save tdwesten/a44b1121d35fe2af9e2ec7cb384e40ec to your computer and use it in GitHub Desktop.
Ember inline-truncate modifier
import { Owner } from '@ember/test-helpers/build-owner';
import Modifier from 'ember-modifier';
export interface InlineTruncateSignature {
Args: null;
Element: HTMLCanvasElement;
}
export default class InlineTruncate extends Modifier<InlineTruncateSignature> {
declare element: HTMLCanvasElement;
declare timeoutId: number;
declare originalText: string;
testString = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
declare testStringLength: number;
declare singleCharWidth: number;
constructor(owner: Owner, args: any) {
super(owner, args);
// run the modifier window resize event listener
window.addEventListener('resize', () => {
clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(() => {
this.modify(this.element);
}, 5);
});
}
/**
* This function takes a canvas element and applies a text ellipsis to it if it
* is too long to fit inside of it.
* @param element The canvas element to apply the text ellipsis to.
*/
modify(element: HTMLCanvasElement) {
this.element = element;
const lastChild = element.lastElementChild;
const workingElement = lastChild || element;
const textContent = workingElement?.textContent?.trim();
if (this.originalText === undefined && textContent) {
this.originalText = textContent;
}
if (!workingElement) {
return textContent;
}
if (!this.singleCharWidth) {
const testElement = document.createElement('span');
testElement.style.visibility = 'hidden';
testElement.style.position = 'absolute';
testElement.style.whiteSpace = 'nowrap';
testElement.innerText = this.testString;
element.appendChild(testElement);
this.singleCharWidth = testElement.offsetWidth / this.testString.length;
element.removeChild(testElement);
}
const maxChars = Math.floor(element.clientWidth / this.singleCharWidth);
if (
this.originalText &&
this.originalText.length > maxChars &&
this.originalText.length > 30
) {
const maxInlineChars = Math.floor(maxChars / 2);
const firstHalf = this.originalText.slice(0, maxInlineChars);
const secondHalf = this.originalText.slice(-maxInlineChars);
workingElement.textContent = `${firstHalf}...${secondHalf}`;
} else {
if (this.originalText) {
workingElement.textContent = this.originalText;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment