break-styled-lines
Version:
Add newlines to a string of text given a font style and width
123 lines (121 loc) • 4.24 kB
JavaScript
var __defProp = Object.defineProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/break-lines.ts
__markAsModule(exports);
__export(exports, {
default: () => break_lines_default
});
function checkFontForBlinkMacSystemFont(font) {
if (font.includes("BlinkMacSystemFont")) {
console.warn("break-styled-lines: Using BlinkMacSystemFont can cause Chrome to crash in certain environments!");
}
}
function isStringArray(text) {
return Array.isArray(text) && (text.length > 0 ? typeof text[0] === "string" : true);
}
function isTextDescriptorArray(text) {
return Array.isArray(text) && (text.length > 0 ? !isStringArray(text) : true);
}
function withNewLines(descriptor, width, startingX, ctx) {
const elements = descriptor.text.split("").reduce((elements2, char) => {
const runningElement = elements2[elements2.length - 1] || "";
const lastChar = runningElement.slice(-1);
if (char === " " && lastChar !== " ") {
return [...elements2, char];
}
if (char !== " " && lastChar === " ") {
return [...elements2, char];
}
return [...elements2.slice(0, -1), `${runningElement}${char}`];
}, []);
const { lastLineWidth, lines } = elements.reduce((result, element) => {
ctx.font = descriptor.font;
const { width: elementWidth } = ctx.measureText(element);
const completeTextWidth = result.lastLineWidth + elementWidth;
const itFits = completeTextWidth <= width;
if (itFits) {
const appendedLine = [...result.lines.slice(-1), element].join("");
return {
lastLineWidth: completeTextWidth,
lines: [...result.lines.slice(0, -1), appendedLine]
};
}
if (elementWidth > width && result.lastLineWidth === 0) {
return {
lastLineWidth: elementWidth,
lines: [...result.lines.slice(0, -1), element]
};
}
const previousLine = result.lines.slice(-1).join("");
const precedingLines = [
...result.lines.slice(0, -1),
previousLine.trimEnd()
];
if (element.trim().length === 0) {
return {
lastLineWidth: 0,
lines: [...precedingLines, ""]
};
}
return {
lastLineWidth: elementWidth,
lines: [...precedingLines, element]
};
}, { lastLineWidth: startingX, lines: [] });
return { lastLineWidth, text: lines.join("\n") };
}
function breakLines(descriptors, width) {
const supportsOffscreenCanvas = "OffscreenCanvas" in window;
const canvasEl = document.createElement("canvas");
const canvas = supportsOffscreenCanvas ? canvasEl.transferControlToOffscreen() : canvasEl;
canvas.width = width;
const ctx = canvas.getContext("2d");
if (ctx) {
return descriptors.reduce((result, descriptor) => {
const { lastLineWidth, text } = withNewLines(descriptor, width, result.lastLineWidth, ctx);
return {
lastLineWidth,
lines: [...result.lines, text]
};
}, { lastLineWidth: 0, lines: [] }).lines;
}
console.warn("No canvas context was found, so the string was left as is!");
return descriptors.map(({ text }) => text);
}
function toTextDescriptors(text, defaultFont) {
if (isTextDescriptorArray(text)) {
return text.map(({ text: text2, font }) => ({
text: stripNewlines(text2),
font: font || defaultFont
}));
}
if (isStringArray(text)) {
return text.map((member) => ({
text: stripNewlines(member),
font: defaultFont
}));
}
return [{ text: stripNewlines(text), font: defaultFont }];
}
var newlineRegex = /(\r\n|\n|\r)/gm;
function stripNewlines(text) {
return text.replace(newlineRegex, " ");
}
function breakLinesEntry(text, width, font) {
checkFontForBlinkMacSystemFont(font);
const descriptors = toTextDescriptors(text, font);
if (isStringArray(text)) {
return breakLines(descriptors, width);
}
if (isTextDescriptorArray(text)) {
return breakLines(descriptors, width);
}
return breakLines(descriptors, width)[0];
}
var break_lines_default = breakLinesEntry;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {});