mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-12 06:28:40 +00:00
Horiz brace (#1332)
* Move "horizBrace" to functions/horizBrace.js. * Add flow types to functions/horizBrace.js. * Remove blank lines and warning comment in functions.js. * Remove import comments and alphabetize functions/ imports in functions.js.
This commit is contained in:
committed by
Kevin Barabash
parent
28dfa91fb5
commit
ff8ca60584
@@ -13,7 +13,6 @@ import Style from "./Style";
|
||||
import buildCommon from "./buildCommon";
|
||||
import domTree from "./domTree";
|
||||
import utils from "./utils";
|
||||
import stretchy from "./stretchy";
|
||||
import {spacings, tightSpacings} from "./spacingData";
|
||||
|
||||
const makeSpan = buildCommon.makeSpan;
|
||||
@@ -415,100 +414,6 @@ export const groupTypes = {
|
||||
[], options);
|
||||
}
|
||||
},
|
||||
|
||||
horizBrace(group, options) {
|
||||
const style = options.style;
|
||||
|
||||
const hasSupSub = (group.type === "supsub");
|
||||
let supSubGroup;
|
||||
let newOptions;
|
||||
if (hasSupSub) {
|
||||
// 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.
|
||||
if (group.value.sup) {
|
||||
newOptions = options.havingStyle(style.sup());
|
||||
supSubGroup = buildGroup(group.value.sup, newOptions, options);
|
||||
} else {
|
||||
newOptions = options.havingStyle(style.sub());
|
||||
supSubGroup = buildGroup(group.value.sub, newOptions, options);
|
||||
}
|
||||
group = group.value.base;
|
||||
}
|
||||
|
||||
// Build the base group
|
||||
const body = buildGroup(
|
||||
group.value.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.value.isOver) {
|
||||
vlist = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [
|
||||
{type: "elem", elem: body},
|
||||
{type: "kern", size: 0.1},
|
||||
{type: "elem", elem: braceBody},
|
||||
],
|
||||
}, options);
|
||||
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);
|
||||
vlist.children[0].children[0].children[0].classes.push("svg-align");
|
||||
}
|
||||
|
||||
if (hasSupSub) {
|
||||
// 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 = makeSpan(
|
||||
["mord", (group.value.isOver ? "mover" : "munder")],
|
||||
[vlist], options);
|
||||
|
||||
if (group.value.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 makeSpan(["mord", (group.value.isOver ? "mover" : "munder")],
|
||||
[vlist], options);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -13,7 +13,6 @@ import ParseError from "./ParseError";
|
||||
import Style from "./Style";
|
||||
import symbols from "./symbols";
|
||||
import utils from "./utils";
|
||||
import stretchy from "./stretchy";
|
||||
|
||||
/**
|
||||
* Takes a symbol and converts it into a MathML text node after performing
|
||||
@@ -272,14 +271,6 @@ groupTypes.spacing = function(group) {
|
||||
return node;
|
||||
};
|
||||
|
||||
groupTypes.horizBrace = function(group, options) {
|
||||
const accentNode = stretchy.mathMLnode(group.value.label);
|
||||
return new mathMLTree.MathNode(
|
||||
(group.value.isOver ? "mover" : "munder"),
|
||||
[buildGroup(group.value.base, options), accentNode]
|
||||
);
|
||||
};
|
||||
|
||||
groupTypes.tag = function(group, options) {
|
||||
const table = new mathMLTree.MathNode("mtable", [
|
||||
new mathMLTree.MathNode("mlabeledtr", [
|
||||
|
121
src/functions.js
121
src/functions.js
@@ -1,111 +1,40 @@
|
||||
// @flow
|
||||
/** Include this to ensure that all functions are defined. */
|
||||
import {
|
||||
default as _defineFunction,
|
||||
_functions,
|
||||
} from "./defineFunction";
|
||||
|
||||
import type {FunctionPropSpec, FunctionHandler} from "./defineFunction";
|
||||
import type {NodeType} from "./ParseNode";
|
||||
|
||||
// WARNING: New functions should be added to src/functions and imported here.
|
||||
import {_functions} from "./defineFunction";
|
||||
|
||||
const functions = _functions;
|
||||
export default functions;
|
||||
|
||||
// Define a convenience function that mimcs the old semantics of defineFunction
|
||||
// to support existing code so that we can migrate it a little bit at a time.
|
||||
const defineFunction = function<NODETYPE: NodeType>(
|
||||
// Type of node data output by the function handler. This is required to aid
|
||||
// type inference of the actual function output.
|
||||
type: NODETYPE,
|
||||
names: string[],
|
||||
props: FunctionPropSpec,
|
||||
handler: ?FunctionHandler<NODETYPE>, // null only if handled in parser
|
||||
) {
|
||||
_defineFunction({type, names, props, handler});
|
||||
};
|
||||
|
||||
// TODO(kevinb): have functions return an object and call defineFunction with
|
||||
// that object in this file instead of relying on side-effects.
|
||||
import "./functions/sqrt";
|
||||
|
||||
import "./functions/color";
|
||||
|
||||
import "./functions/text";
|
||||
|
||||
import "./functions/math";
|
||||
|
||||
import "./functions/enclose";
|
||||
|
||||
import "./functions/overline";
|
||||
|
||||
import "./functions/underline";
|
||||
|
||||
import "./functions/rule";
|
||||
|
||||
import "./functions/kern";
|
||||
|
||||
import "./functions/phantom";
|
||||
|
||||
import "./functions/mclass";
|
||||
|
||||
import "./functions/mod";
|
||||
|
||||
import "./functions/op";
|
||||
|
||||
import "./functions/operatorname";
|
||||
|
||||
import "./functions/genfrac";
|
||||
|
||||
import "./functions/lap";
|
||||
|
||||
import "./functions/smash";
|
||||
|
||||
import "./functions/delimsizing";
|
||||
|
||||
import "./functions/sizing";
|
||||
|
||||
import "./functions/styling";
|
||||
|
||||
import "./functions/font";
|
||||
|
||||
import "./functions/accent";
|
||||
|
||||
// Horizontal stretchy braces
|
||||
defineFunction("horizBrace", [
|
||||
"\\overbrace", "\\underbrace",
|
||||
], {
|
||||
numArgs: 1,
|
||||
}, function(context, args) {
|
||||
const base = args[0];
|
||||
return {
|
||||
type: "horizBrace",
|
||||
label: context.funcName,
|
||||
isOver: /^\\over/.test(context.funcName),
|
||||
base: base,
|
||||
};
|
||||
});
|
||||
|
||||
// Stretchy accents under the body
|
||||
import "./functions/accentunder";
|
||||
|
||||
// Stretch arrows
|
||||
import "./functions/arrow";
|
||||
|
||||
// Row and line breaks
|
||||
import "./functions/color";
|
||||
import "./functions/cr";
|
||||
|
||||
// Environment delimiters
|
||||
import "./functions/delimsizing";
|
||||
import "./functions/enclose";
|
||||
import "./functions/environment";
|
||||
|
||||
// Box manipulation
|
||||
import "./functions/raisebox";
|
||||
|
||||
import "./functions/verb";
|
||||
|
||||
// Hyperlinks
|
||||
import "./functions/font";
|
||||
import "./functions/genfrac";
|
||||
import "./functions/horizBrace";
|
||||
import "./functions/href";
|
||||
|
||||
// MathChoice
|
||||
import "./functions/kern";
|
||||
import "./functions/lap";
|
||||
import "./functions/math";
|
||||
import "./functions/mathchoice";
|
||||
import "./functions/mclass";
|
||||
import "./functions/mod";
|
||||
import "./functions/op";
|
||||
import "./functions/operatorname";
|
||||
import "./functions/overline";
|
||||
import "./functions/phantom";
|
||||
import "./functions/raisebox";
|
||||
import "./functions/rule";
|
||||
import "./functions/sizing";
|
||||
import "./functions/smash";
|
||||
import "./functions/sqrt";
|
||||
import "./functions/styling";
|
||||
import "./functions/text";
|
||||
import "./functions/underline";
|
||||
import "./functions/verb";
|
||||
|
142
src/functions/horizBrace.js
Normal file
142
src/functions/horizBrace.js
Normal file
@@ -0,0 +1,142 @@
|
||||
// @flow
|
||||
import defineFunction from "../defineFunction";
|
||||
import buildCommon from "../buildCommon";
|
||||
import mathMLTree from "../mathMLTree";
|
||||
import stretchy from "../stretchy";
|
||||
import Style from "../Style";
|
||||
import {assertNodeType, checkNodeType} from "../ParseNode";
|
||||
|
||||
import * as html from "../buildHTML";
|
||||
import * as mml from "../buildMathML";
|
||||
|
||||
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.
|
||||
function htmlBuilder(grp: ParseNode<*>, options) {
|
||||
const style = options.style;
|
||||
|
||||
// Pull out the `ParseNode<"horizBrace">` if `grp` is a "supsub" node.
|
||||
let supSubGroup;
|
||||
let group: ParseNode<"horizBrace">;
|
||||
const supSub = checkNodeType(grp, "supsub");
|
||||
if (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 = supSub.value.sup ?
|
||||
html.buildGroup(
|
||||
supSub.value.sup, options.havingStyle(style.sup()), options) :
|
||||
html.buildGroup(
|
||||
supSub.value.sub, options.havingStyle(style.sub()), options);
|
||||
// The supsub `base` must be non-null in this context. Otherwise,
|
||||
// this `htmlBuilder` handler wouldn't have been invoked.
|
||||
// $FlowFixMe
|
||||
const base: ParseNode<*> = supSub.value.base;
|
||||
group = assertNodeType(base, "horizBrace");
|
||||
} else {
|
||||
group = assertNodeType(grp, "horizBrace");
|
||||
}
|
||||
|
||||
// Build the base group
|
||||
const body = html.buildGroup(
|
||||
group.value.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.value.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.value.isOver ? "mover" : "munder")],
|
||||
[vlist], options);
|
||||
|
||||
if (group.value.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.value.isOver ? "mover" : "munder")], [vlist], options);
|
||||
}
|
||||
|
||||
// Horizontal stretchy braces
|
||||
defineFunction({
|
||||
type: "horizBrace",
|
||||
names: ["\\overbrace", "\\underbrace"],
|
||||
props: {
|
||||
numArgs: 1,
|
||||
},
|
||||
handler(context, args) {
|
||||
return {
|
||||
type: "horizBrace",
|
||||
label: context.funcName,
|
||||
isOver: /^\\over/.test(context.funcName),
|
||||
base: args[0],
|
||||
};
|
||||
},
|
||||
htmlBuilder,
|
||||
mathmlBuilder(group, options) {
|
||||
const accentNode = stretchy.mathMLnode(group.value.label);
|
||||
return new mathMLTree.MathNode(
|
||||
(group.value.isOver ? "mover" : "munder"),
|
||||
[mml.buildGroup(group.value.base, options), accentNode]
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
@@ -168,7 +168,8 @@ const groupLength = function(arg: ParseNode<*>): number {
|
||||
};
|
||||
|
||||
const svgSpan = function(
|
||||
group: ParseNode<"accent"> | ParseNode<"accentUnder"> | ParseNode<"xArrow">,
|
||||
group: ParseNode<"accent"> | ParseNode<"accentUnder"> | ParseNode<"xArrow">
|
||||
| ParseNode<"horizBrace">,
|
||||
options: Options,
|
||||
): DomSpan | SvgSpan {
|
||||
// Create a span with inline SVG for the element.
|
||||
|
Reference in New Issue
Block a user