Socket
Socket
Sign inDemoInstall

break-styled-lines

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

break-styled-lines - npm Package Compare versions

Comparing version 1.1.2 to 1.2.0

132

dist-src/break-lines.js

@@ -1,45 +0,8 @@

function isArray(text) {
return Array.isArray(text);
function isStringArray(text) {
return Array.isArray(text) && text.every((member) => typeof member === 'string');
}
function insertNewlineAtPosition(position, arrayOfStrings) {
const { indexToInsertInto, localPosition } = arrayOfStrings.reduce(({ indexToInsertInto, localPosition, lengthOfPreceding }, string, i) => {
const totalLength = string.length + lengthOfPreceding;
if (!indexToInsertInto && !localPosition && position < totalLength) {
return {
indexToInsertInto: i,
localPosition: position - lengthOfPreceding,
lengthOfPreceding: totalLength
};
}
return {
indexToInsertInto,
localPosition,
lengthOfPreceding: totalLength
};
}, { indexToInsertInto: 0, localPosition: 0, lengthOfPreceding: 0 });
return (arrayOfStrings
.map((string, i) => {
if (i === indexToInsertInto) {
return (string.slice(0, localPosition) + "\n" + string.slice(localPosition));
}
return string;
})
// This is to remove whitespace adjacent to newlines, but to preserve starting or trailing whitespace
.map(string => string
.split("\n")
.map((str, i, strs) => {
if (i < strs.length - 2 && i > 0) {
return str.trim();
}
if (i < strs.length - 2) {
return str.trimRight();
}
else if (i > 0) {
return str.trimLeft();
}
return str;
})
.join("\n")));
function isTextDescriptorArray(text) {
return Array.isArray(text) && !isStringArray(text);
}
function breakLines(text, width, font) {
function breakLines(descriptors, width) {
const supportsOffscreenCanvas = "OffscreenCanvas" in window;

@@ -53,25 +16,44 @@ const canvasEl = document.createElement("canvas");

if (ctx) {
ctx.font = font;
const brokenWords = text.split(" ").reduce((accumulator, word) => {
// get the last element of the accumulator
const [lastLine] = accumulator.slice(-1);
// add the word to it
const maybeNextLine = [...lastLine, word].join(" ");
// see if it fits within the width
let { width: textWidth } = ctx.measureText(maybeNextLine);
// if it does, append to the last element
if (textWidth <= width) {
return [...accumulator.slice(0, -1), [...lastLine, word]];
}
if (lastLine.length === 0) {
return [...accumulator.slice(0, -1), [word]];
}
// if not, create a new array containing the word as the last element
return [...accumulator, [word]];
}, [[]]);
return brokenWords.map(line => line.join(" ")).join("\n");
return descriptors.reduce((result, { text, font }, descIndex) => {
const lastLine = result[result.length - 1];
const lastLineSplit = lastLine ? lastLine.split('\n') : [];
const runningLine = lastLineSplit.length > 0 ? lastLineSplit[lastLineSplit.length - 1] : "";
const prevFont = descriptors[descIndex - 1] ? descriptors[descIndex - 1].font : font;
const brokenWords = text.split(" ").reduce((accumulator, word, i) => {
// get the last element of the accumulator
const [lastLine] = accumulator.slice(-1);
// add the word to it
const maybeNextLine = [...lastLine, word].join(" ");
// see if it fits within the width
ctx.font = prevFont;
const { width: prevTextWidth } = ctx.measureText(runningLine);
ctx.font = font;
const { width: textWidth } = ctx.measureText(maybeNextLine);
const totalWidth = i === 0 ? prevTextWidth + textWidth : textWidth;
// if it does, append to the last element
if (totalWidth <= width) {
return [...accumulator.slice(0, -1), [...lastLine, word]];
}
// Handle case of the first word being too long for a line
if (totalWidth > width && i === 0 && runningLine === "") {
return [...accumulator.slice(0, -1), [...lastLine, word]];
}
// if not, create a new array containing the word as the last element
return [...accumulator, [word]];
}, [[]]);
return [...result, brokenWords.map(line => line.join(" ")).join("\n")];
}, []);
}
console.warn("No canvas context was found, so the string was left as is!");
return text;
return descriptors.map(({ text }) => text);
}
function toTextDescriptors(text, defaultFont) {
if (isTextDescriptorArray(text)) {
return text.map(({ text, font }) => ({ text, font: font || defaultFont }));
}
if (isStringArray(text)) {
return text.map((member) => ({ text: member, font: defaultFont }));
}
return [{ text, font: defaultFont }];
}
/**

@@ -86,23 +68,11 @@ * Breaks a string into lines given a width and style for the text.

function breakLinesEntry(text, width, font) {
if (isArray(text)) {
/*
['hello there ', 'my good friend, ', 'how are you today?']
+ ['hello there my good\n friend, how are you\n today?']
= ['hello there ', 'my good\n friend, ', 'how are you\n today?']
*/
const withNewLines = breakLines(text.join(""), width, font);
const newLinePositions = withNewLines
.split("")
.reduce((positions, char, i) => {
if (char === "\n") {
return [...positions, i];
}
return positions;
}, []);
return newLinePositions.reduce((result, position) => {
return insertNewlineAtPosition(position, result);
}, text);
const descriptors = toTextDescriptors(text, font);
if (isStringArray(text)) {
return breakLines(descriptors, width);
}
return breakLines(text, width, font);
if (isTextDescriptorArray(text)) {
return breakLines(descriptors, width);
}
return breakLines(descriptors, width)[0];
}
export default breakLinesEntry;

@@ -0,3 +1,8 @@

declare type TextDescriptor = {
text: string;
font?: string;
};
declare function breakLinesEntry(text: string, width: number, font: string): string;
declare function breakLinesEntry(text: string[], width: number, font: string): string[];
declare function breakLinesEntry(text: TextDescriptor[], width: number, font: string): string[];
export default breakLinesEntry;

@@ -1,45 +0,8 @@

function isArray(text) {
return Array.isArray(text);
function isStringArray(text) {
return Array.isArray(text) && text.every((member) => typeof member === 'string');
}
function insertNewlineAtPosition(position, arrayOfStrings) {
const { indexToInsertInto, localPosition } = arrayOfStrings.reduce(({ indexToInsertInto, localPosition, lengthOfPreceding }, string, i) => {
const totalLength = string.length + lengthOfPreceding;
if (!indexToInsertInto && !localPosition && position < totalLength) {
return {
indexToInsertInto: i,
localPosition: position - lengthOfPreceding,
lengthOfPreceding: totalLength
};
}
return {
indexToInsertInto,
localPosition,
lengthOfPreceding: totalLength
};
}, { indexToInsertInto: 0, localPosition: 0, lengthOfPreceding: 0 });
return (arrayOfStrings
.map((string, i) => {
if (i === indexToInsertInto) {
return (string.slice(0, localPosition) + "\n" + string.slice(localPosition));
}
return string;
})
// This is to remove whitespace adjacent to newlines, but to preserve starting or trailing whitespace
.map(string => string
.split("\n")
.map((str, i, strs) => {
if (i < strs.length - 2 && i > 0) {
return str.trim();
}
if (i < strs.length - 2) {
return str.trimRight();
}
else if (i > 0) {
return str.trimLeft();
}
return str;
})
.join("\n")));
function isTextDescriptorArray(text) {
return Array.isArray(text) && !isStringArray(text);
}
function breakLines(text, width, font) {
function breakLines(descriptors, width) {
const supportsOffscreenCanvas = "OffscreenCanvas" in window;

@@ -53,25 +16,44 @@ const canvasEl = document.createElement("canvas");

if (ctx) {
ctx.font = font;
const brokenWords = text.split(" ").reduce((accumulator, word) => {
// get the last element of the accumulator
const [lastLine] = accumulator.slice(-1);
// add the word to it
const maybeNextLine = [...lastLine, word].join(" ");
// see if it fits within the width
let { width: textWidth } = ctx.measureText(maybeNextLine);
// if it does, append to the last element
if (textWidth <= width) {
return [...accumulator.slice(0, -1), [...lastLine, word]];
}
if (lastLine.length === 0) {
return [...accumulator.slice(0, -1), [word]];
}
// if not, create a new array containing the word as the last element
return [...accumulator, [word]];
}, [[]]);
return brokenWords.map(line => line.join(" ")).join("\n");
return descriptors.reduce((result, { text, font }, descIndex) => {
const lastLine = result[result.length - 1];
const lastLineSplit = lastLine ? lastLine.split('\n') : [];
const runningLine = lastLineSplit.length > 0 ? lastLineSplit[lastLineSplit.length - 1] : "";
const prevFont = descriptors[descIndex - 1] ? descriptors[descIndex - 1].font : font;
const brokenWords = text.split(" ").reduce((accumulator, word, i) => {
// get the last element of the accumulator
const [lastLine] = accumulator.slice(-1);
// add the word to it
const maybeNextLine = [...lastLine, word].join(" ");
// see if it fits within the width
ctx.font = prevFont;
const { width: prevTextWidth } = ctx.measureText(runningLine);
ctx.font = font;
const { width: textWidth } = ctx.measureText(maybeNextLine);
const totalWidth = i === 0 ? prevTextWidth + textWidth : textWidth;
// if it does, append to the last element
if (totalWidth <= width) {
return [...accumulator.slice(0, -1), [...lastLine, word]];
}
// Handle case of the first word being too long for a line
if (totalWidth > width && i === 0 && runningLine === "") {
return [...accumulator.slice(0, -1), [...lastLine, word]];
}
// if not, create a new array containing the word as the last element
return [...accumulator, [word]];
}, [[]]);
return [...result, brokenWords.map(line => line.join(" ")).join("\n")];
}, []);
}
console.warn("No canvas context was found, so the string was left as is!");
return text;
return descriptors.map(({ text }) => text);
}
function toTextDescriptors(text, defaultFont) {
if (isTextDescriptorArray(text)) {
return text.map(({ text, font }) => ({ text, font: font || defaultFont }));
}
if (isStringArray(text)) {
return text.map((member) => ({ text: member, font: defaultFont }));
}
return [{ text, font: defaultFont }];
}
/**

@@ -86,22 +68,10 @@ * Breaks a string into lines given a width and style for the text.

function breakLinesEntry(text, width, font) {
if (isArray(text)) {
/*
['hello there ', 'my good friend, ', 'how are you today?']
+ ['hello there my good\n friend, how are you\n today?']
= ['hello there ', 'my good\n friend, ', 'how are you\n today?']
*/
const withNewLines = breakLines(text.join(""), width, font);
const newLinePositions = withNewLines
.split("")
.reduce((positions, char, i) => {
if (char === "\n") {
return [...positions, i];
}
return positions;
}, []);
return newLinePositions.reduce((result, position) => {
return insertNewlineAtPosition(position, result);
}, text);
const descriptors = toTextDescriptors(text, font);
if (isStringArray(text)) {
return breakLines(descriptors, width);
}
return breakLines(text, width, font);
if (isTextDescriptorArray(text)) {
return breakLines(descriptors, width);
}
return breakLines(descriptors, width)[0];
}

@@ -108,0 +78,0 @@

{
"name": "break-styled-lines",
"description": "Add newlines to a string of text given a font style and width",
"version": "1.1.2",
"version": "1.2.0",
"license": "MIT",

@@ -6,0 +6,0 @@ "files": [

# break-styled-lines
This package is useful for when you want to know where linebreaks will occur in text given a font style (e.g. `16pt italic Georgia`) and the width of its container. Uses the Canvas API to measure text, so it's quite fast. Uses an OffscreenCanvas if the browser supports it.
This package is useful for when you want to know where linebreaks will occur in text given a font style (e.g. `16pt italic Georgia`) and the width of its container.
## Example
This utility also supports some more complex use-cases:
```
import breakLines from 'break-styled-lines';
- Adding linebreaks to a list of strings as though they were a single string, and returning the text back as arrays.
- Adding linebreaks to a body of text which does not have consistent styling throughout (e.g. one much larger word in the middle of a sentence).
Uses the Canvas API to measure text, so it's quite fast. Uses an OffscreenCanvas if the browser supports it.
Comes with typescript types.
## Basic example
```ts
import breakLines from "break-styled-lines";
const textWithLineBreaks = breakLines(

@@ -36,11 +45,65 @@ "Good day to you my friends! What ails you on this day?",

The single `breakLines` export supports three different cases:
### Single string
```ts
breakLines(
// The text you'd like to insert newlines into
text: string
// The width constraining the text
width: number
// The font style of the text (a value of the CSS font property e.g. 10px bold serif)
style: string
): string | string[]
```
```ts
breakLines("Good morrow my good man!", 100, "16pt serif");
```
### Array of strings
```ts
breakLines(
// The string you'd like to break into lines. Can also be an array of strings, which will be treated as a single run of text, and then split back apart again.
text: string | string[]
// An array of strings, which will be treated as a single run of text, and then split back apart again before being returned.
text: string[]
// The width constraining the text
width: number
// The style of the text (a value of the CSS font property e.g. 10px bold serif)
// The font style of the text (a value of the CSS font property e.g. 10px bold serif)
style: string
): string | string[]
```
````ts
breakLines(["Good morrow my good man!", " What brings you to our corner of the world?"], 100, "16pt serif")
```ts
### Text descriptors
```ts
breakLines(
// An array of descriptors, which will be treated as a single run of text, and then split back apart again before being returned.
// { text: string, font?: string }
text: TextDescriptor[]
// The width constraining the text
width: number
// The default font style of the text which will be used if the descriptor is not provided one (a value of the CSS font property e.g. 10px bold serif)
style: string
): string | string[]
````
```ts
breakLines(
[
{ text: "Good morrow my good man!" },
{ text: " What brings you to our corner of the world?" },
{
text: " Our selection of the finest smoked cheeses, you say?!",
font: "36pt bold Impact",
},
],
100,
"16pt serif"
);
```

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc