Files
paige-hugo/paige/node_modules/katex/src/functions/horizBrace.js
2023-03-27 20:36:59 -07:00

138 lines
4.9 KiB
JavaScript

// @flow
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import stretchy from "../stretchy";
import Style from "../Style";
import {assertNodeType} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type {HtmlBuilderSupSub, MathMLBuilder} from "../defineFunction";
import type {ParseNode} from "../parseNode";
// NOTE: Unlike most `htmlBuilder`s, this one handles not only "horizBrace", but
// also "supsub" since an over/underbrace can affect super/subscripting.
export const htmlBuilder: HtmlBuilderSupSub<"horizBrace"> = (grp, options) => {
const style = options.style;
// Pull out the `ParseNode<"horizBrace">` if `grp` is a "supsub" node.
let supSubGroup;
let group: ParseNode<"horizBrace">;
if (grp.type === "supsub") {
// Ref: LaTeX source2e: }}}}\limits}
// i.e. LaTeX treats the brace similar to an op and passes it
// with \limits, so we need to assign supsub style.
supSubGroup = grp.sup ?
html.buildGroup(grp.sup, options.havingStyle(style.sup()), options) :
html.buildGroup(grp.sub, options.havingStyle(style.sub()), options);
group = assertNodeType(grp.base, "horizBrace");
} else {
group = assertNodeType(grp, "horizBrace");
}
// Build the base group
const body = html.buildGroup(
group.base, options.havingBaseStyle(Style.DISPLAY));
// Create the stretchy element
const braceBody = stretchy.svgSpan(group, options);
// Generate the vlist, with the appropriate kerns ┏━━━━━━━━┓
// This first vlist contains the content and the brace: equation
let vlist;
if (group.isOver) {
vlist = buildCommon.makeVList({
positionType: "firstBaseline",
children: [
{type: "elem", elem: body},
{type: "kern", size: 0.1},
{type: "elem", elem: braceBody},
],
}, options);
// $FlowFixMe: Replace this with passing "svg-align" into makeVList.
vlist.children[0].children[0].children[1].classes.push("svg-align");
} else {
vlist = buildCommon.makeVList({
positionType: "bottom",
positionData: body.depth + 0.1 + braceBody.height,
children: [
{type: "elem", elem: braceBody},
{type: "kern", size: 0.1},
{type: "elem", elem: body},
],
}, options);
// $FlowFixMe: Replace this with passing "svg-align" into makeVList.
vlist.children[0].children[0].children[0].classes.push("svg-align");
}
if (supSubGroup) {
// To write the supsub, wrap the first vlist in another vlist:
// They can't all go in the same vlist, because the note might be
// wider than the equation. We want the equation to control the
// brace width.
// note long note long note
// ┏━━━━━━━━┓ or ┏━━━┓ not ┏━━━━━━━━━┓
// equation eqn eqn
const vSpan = buildCommon.makeSpan(
["mord", (group.isOver ? "mover" : "munder")],
[vlist], options);
if (group.isOver) {
vlist = buildCommon.makeVList({
positionType: "firstBaseline",
children: [
{type: "elem", elem: vSpan},
{type: "kern", size: 0.2},
{type: "elem", elem: supSubGroup},
],
}, options);
} else {
vlist = buildCommon.makeVList({
positionType: "bottom",
positionData: vSpan.depth + 0.2 + supSubGroup.height +
supSubGroup.depth,
children: [
{type: "elem", elem: supSubGroup},
{type: "kern", size: 0.2},
{type: "elem", elem: vSpan},
],
}, options);
}
}
return buildCommon.makeSpan(
["mord", (group.isOver ? "mover" : "munder")], [vlist], options);
};
const mathmlBuilder: MathMLBuilder<"horizBrace"> = (group, options) => {
const accentNode = stretchy.mathMLnode(group.label);
return new mathMLTree.MathNode(
(group.isOver ? "mover" : "munder"),
[mml.buildGroup(group.base, options), accentNode]
);
};
// Horizontal stretchy braces
defineFunction({
type: "horizBrace",
names: ["\\overbrace", "\\underbrace"],
props: {
numArgs: 1,
},
handler({parser, funcName}, args) {
return {
type: "horizBrace",
mode: parser.mode,
label: funcName,
isOver: /^\\over/.test(funcName),
base: args[0],
};
},
htmlBuilder,
mathmlBuilder,
});