Published
Edited
Oct 30, 2020
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function computeN(blockWidth, textWidth) {
if (textWidth <= blockWidth) return 1; // no linebreaks needed

return Math.ceil(textWidth / blockWidth);
}
Insert cell
computeN(300, 500)
Insert cell
Insert cell
function computeBreakWidth(blockWidth, textWidth) {
return Math.ceil(textWidth / computeN(blockWidth, textWidth));
}
Insert cell
computeBreakWidth(300, 700)
Insert cell
Insert cell
function* badCalculate(el, text) {
yield el; // we "blink" the element to get it's inner width
const intElemClientWidth = el.clientWidth;
const textWidth = Math.ceil(measure(text, getUsedFont(el)));
const mw = computeBreakWidth(intElemClientWidth, textWidth);
const ht = html`<div style="max-width: ${mw}px; line-break: anywhere; border: 1px solid black">${text}</div>`;

ht.value = { mw, textWidth, intElemClientWidth };
yield ht;
}
Insert cell
badCalculate(
html`<div style="width: 300px"></div>`,
"This is a multiline text placed inside a 300px div"
)
Insert cell
Insert cell
calculate(300, "This is a multiline text placed inside 300px")
Insert cell
Insert cell
{
const el = htmlb`<div style="width: 300px; border: 1px solid black">${"This is a multiline text placed inside a 300px div"}</div>`;
yield el;
el.update();
}
Insert cell
Insert cell
htmlb`<div style="border: 1px solid black" ${300}>${"This is a multiline text placed inside a 300px div"}</div>`
Insert cell
Insert cell
Insert cell
{
const hello = htmlb`<div style="border: 1px solid black">${Array(30).join(
' hello'
)}</div>`;
const small = htmlb`<div style="border: 1px solid black">${"This is a multiline text placed inside a 300px div"}</div>`;
yield example(
"good",
html`<div style="display: flex; width: 100%">${hello}${small}</div>`
);
hello.update();
small.update();
}
Insert cell
Insert cell
Insert cell
Insert cell
function htmlb(el, ...args) {
el = html`${el.join('')}`;

let text = args[0];
let elWidth;
mutable debug.args = args;
mutable debug.elWidth = elWidth;
for (const arg of args) {
if (typeof arg === "number") {
elWidth = arg;
} else {
text = arg;
}
}
function update() {
if (!elWidth) {
const temp = el.append(text); // we put the text
// calculate the size
elWidth = el.clientWidth;
mutable debug.el = el;
// el.replaceChildren();
while (el.lastChild) {
// replaceChildren Polyfill
el.removeChild(el.lastChild);
}
}

const fragment = calculate(elWidth, text, { el }).reduce(
(df, curr, idx, src) => {
df.append(curr);
if (idx + 1 < src.length) df.append(html`<br/>`);
return df;
},
new DocumentFragment()
);

el.append(fragment);
}

mutable debug.el = elWidth;
if (elWidth) {
const fragment = calculate(elWidth, text).reduce((df, curr, idx, src) => {
df.append(curr);
if (idx + 1 < src.length) df.append(html`<br/>`);
return df;
}, new DocumentFragment());

el.append(fragment);
return el;
} else return Object.assign(el, { update }); // we "blink" the element to get it's inner width
}
Insert cell
{
const fr = htl.fragment`This is a multiline text<br/>placed inside a 300px div`;
const el = html`<div></div>`;
el.append(fr);
return el;
}
Insert cell
mutable debug = ({})
Insert cell
function calculate(elWidth, text, options = {}) {
let { el, threshold } = {
threshold: 0.2,
el: document.body,
...options
};
let textWidth = Math.ceil(measure(text, getUsedFont(el)));
const n = computeN(elWidth, textWidth);
const part = Math.floor(text.length / n);

let newtext = [text];
let offset = 0;
for (let i = 0; i < n - 1; i++) {
const currentMiddle = part + offset;
const sep = newtext[i];
const before = sep.lastIndexOf(' ', currentMiddle); // closest space before middle
const after = sep.indexOf(' ', currentMiddle + 1); // closest space after middle
let chosen =
before == -1 ||
(after != -1 && currentMiddle - before >= after - currentMiddle)
? after
: before;

if (Math.abs(chosen - currentMiddle) > part * threshold) {
// we hyphenate
offset = 0;
newtext[i] = sep.substr(0, part) + "-";
newtext.push(sep.substr(part));
} else {
offset = currentMiddle - chosen;
newtext[i] = sep.substr(0, chosen);
newtext.push(sep.substr(chosen + 1));
}
}
return newtext;
}
Insert cell
Insert cell
Insert cell
import { html as htl } from "@observablehq/htl"
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more