138 lines
4.9 KiB
JavaScript
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,
|
|
});
|