Public
Edited
Jan 20, 2023
Importers
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
registerStyle(Style)
Insert cell
Insert cell
Tokens = ({
ButtonBG: Color,
Text: { color: "#30300", size: "14px", font: "sans-serif" },
pad: (_) => unit(_ * 4, "px"),
bd: (_) => unit(_ * 2, "px")
})
Insert cell
Style = style(
{
"&": {
background: "${ButtonBG}A0",
cursor: "help",
display: "inline-block",
fontSize: "${Text.size}",
color: "${Text.color}",
fontFamily: "${Text.font}",
borderRadius: "$2bd",
padding: "$2pad $3pad"
},
"& code": {
opacity: 0.5,
fontSize: "${Text.size}"
},
"&:hover": {
background: "${ButtonBG}"
}
},
Tokens
)
Insert cell
Insert cell
Insert cell
style = (rules, context = undefined, name = undefined, parser = parseToken) => {
rules = expandRules(rules, context, parser);
const className = `.${name ? name : alpha(hash(rules))}`;
return reduce(
rules,
(r, v, k) => {
r[k.replace("&", className)] = v;
},
{}
);
}
Insert cell
className = (...rules) =>
[
...rules
.reduce((r, v) => {
for (let k in v) {
const match = k.match(RE_RULEHASH);
if (match) {
const n = match.groups.hash;
r.set(n, true);
}
}
return r;
}, new Map())
.keys()
].join(" ")
Insert cell
Insert cell
registerStyle = (style, name = "default", reset = true) => {
return html`<style>${(style instanceof Array ? style : [style])
.map((_) => compileStyle(_).join("\n"))
.join("\n")}`;
}
Insert cell
Insert cell
Insert cell
compileStyle(
style({
"@": [
"import url('https://fonts.googleapis.com/css2?family=Karla:wght@400;500;600;700;800&display=swap')"
],
"&": {
fontSize: "1rem",
lineHeight: "1.25em",
fontFamily: "$text",
fontWeight: 200,
color: "$text"
}
})
)
Insert cell
compileStatement = (name, value) =>
name === "content"
? `content: ${JSON.stringify(value)};`
: `${name}: ${value};`
Insert cell
compileStyle = (style) =>
reduce(
style,
(r, properties, scope) => {
const p =
properties instanceof Array
? properties
: reduce(
properties,
(r, value, name) => {
r.push(compileStatement(propertyName(name), value));
},
[]
);

if (scope === "@") {
return r.concat(p.map((_) => `@${_};`));
} else {
r.push(`${scope} {${p.join("")}}`);
return r;
}
},
[]
)
Insert cell
Insert cell
Insert cell
expandRules = {
const expandRules = (rules, context = undefined, parser = parseToken) =>
map(rules, (v, k) =>
v instanceof Object
? expandRules(v, context, parser)
: expandProperty(v, k, context, parser)
);
return expandRules;
}
Insert cell
Insert cell
expandProperty = (
value,
name = undefined,
context = undefined,
parser = parseToken
) => {
const res = [];
let match = null;
let offset = 0;
while ((match = RE_TEMPLATE.exec(value)) !== null) {
// We push the inbetween text
res.push(value.substring(offset, match.index));
// We parse the token and push the result
res.push(
parser(
match.groups.token || match.groups.expr,
context,
match.groups.expr ? true : false
)
);
offset = match.index + match[0].length;
}
if (offset === 0) {
return value;
} else {
res.push(value.substring(offset, value.length));
return res.join("");
}
return value;
}
Insert cell
parseToken = (token, context) => {
const match = RE_TOKEN_CHAIN.exec(token);
// We reduce the tokens
return match
? match[0].split(".").reduce((r, v, i) => {
const m = RE_TOKEN.exec(v);
if (m.groups) {
const { number, decimal, unit, token } = m.groups;
const w = number
? evalNumber(
decimal ? parseFloat(number) : parseInt(number),
unit,
r
)
: evalToken(token, r);
return w;
} else {
return r;
}
return r;
}, context)
: token;
}
Insert cell
expandProperty("${text.font}", undefined, {
text: { font: "sans-serif" }
})
Insert cell
expandProperty("$2pad $4pad", undefined, {
pad: (_) => unit(_ * 8, "px")
})
Insert cell
evalNumber = (number, unit, context) =>
context && context[unit] ? context[unit](number) : `${number}${unit || "px"}`
Insert cell
evalToken = (token, context) =>
context && context[token] ? context[token] : `var(--${token})`
Insert cell
Insert cell
RE_TEMPLATE = new RegExp(
"\\$((?<token>[\\w_]+(\\.[\\w_]+)*)|{(?<expr>[^}]+)})",
"g"
)
Insert cell
map({ number: "$10pad", tokenA: "${RED_LT}A10", tokenB: "${text.size}" }, (_) =>
_.match(RE_TEMPLATE)
)
Insert cell
RE_TOKEN_CHAIN = {
const expr = "((\\d+(\\.\\d+)?)(\\w+)|([\\w\\d_]+))";
const chain = `${expr}(\\.${expr})*`;
return new RegExp(chain, "g");
}
Insert cell
RE_TOKEN = new RegExp(
"(?<number>\\d+(?<decimal>\\.\\d+)?)(?<unit>\\w+)|(?<token>[\\w\\d_]+)"
)
Insert cell
RE_RULEHASH = new RegExp("^\\.(?<hash>[A-Za-z0-9]+)")
Insert cell
map({ number: "10pad", tokenA: "RED_LT_A10", tokenB: "REDA0" }, (_) =>
RE_TOKEN.exec(_)
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
numfmt = (value, precision = 0) => {
const p = Math.pow(10, precision);
const v = parseFloat(value);
const w = Math.round(v * p);
const k = `${w}`;
const i = k.length - precision;
return precision && v * p != w
? `${k.substring(0, i) || "0"}.${k.substring(i) || "0"}`
: value;
}
Insert cell
Insert cell
unit(100, "vh")
Insert cell
propertyName = (name) => {
if (name && name.startsWith("--")) return name;
else {
const property = RE_CSS_PROPERTY;
const res = [];
let match = null;
while ((match = property.exec(name)) !== null) {
res.push(match[0].toLowerCase());
}
return res.join("-");
}
};
Insert cell
propertyName("borderRadius")
Insert cell
Insert cell
Insert cell
map({ a: 1, b: 2, c: 3 }, (_) => _ * 10)
Insert cell
Insert cell
reduce({ a: 10, b: 20, c: 30 }, (r, v) => r + v, 0)
Insert cell
Insert cell
hash("One two three four")
Insert cell
Insert cell
alpha(hash("One two three four"))
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